Files
circle-collect/src/components/common/CustomModal.tsx
T

185 lines
6.2 KiB
TypeScript

import React, { useRef, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
Modal,
TouchableOpacity,
Animated,
Dimensions,
KeyboardAvoidingView,
Platform,
TouchableWithoutFeedback,
Keyboard,
} from 'react-native';
import { responsiveFontSize } from 'react-native-responsive-dimensions';
import { useTheme } from '../../hooks/ui/useTheme';
import FontsFamily from '../../constents/FontsFamily';
const { height } = Dimensions.get('window');
type CustomModalProps = {
visible: boolean;
onClose: () => void;
title?: string;
children?: React.ReactNode;
confirmText?: string;
cancelText?: string;
onConfirm?: () => void;
confirmButton?: string;
confirmTextColor?: string;
elevation?: number;
};
const CustomModal: React.FC<CustomModalProps> = ({
visible,
onClose,
title,
children,
confirmText = 'Confirm',
cancelText = 'Cancel',
onConfirm,
confirmTextColor,
elevation = 5,
confirmButton,
}) => {
const theme = useTheme();
const slideAnim = useRef(new Animated.Value(height)).current;
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (visible) {
Animated.parallel([
Animated.timing(slideAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
]).start();
} else {
Animated.parallel([
Animated.timing(slideAnim, {
toValue: height,
duration: 250,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 0,
duration: 250,
useNativeDriver: true,
}),
]).start();
}
}, [fadeAnim, slideAnim, visible]);
return (
<Modal transparent visible={visible} animationType="none">
<Animated.View style={[styles.overlay, { opacity: fadeAnim }]}>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.keyboardContainer}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={styles.keyboardContainer}>
<Animated.View
style={[
styles.modalBox,
{
backgroundColor: theme.colors.surface,
borderTopLeftRadius: theme.borderRadius.xl,
borderTopRightRadius: theme.borderRadius.xl,
transform: [{ translateY: slideAnim }]
},
]}
>
{title && (
<Text style={[styles.title, { color: theme.colors.textPrimary }]}>{title}</Text>
)}
<View style={styles.content}>{children}</View>
<View style={styles.actions}>
<TouchableOpacity
style={[styles.button, { backgroundColor: theme.colors.background, elevation: elevation }]}
onPress={onClose}
>
<Text style={[styles.cancelText, { color: theme.colors.textSecondary }]}>{cancelText}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.button,
{
backgroundColor: confirmButton || theme.colors.primary,
elevation: elevation
}
]}
onPress={onConfirm}
>
<Text
style={[
styles.confirmText,
{ color: confirmTextColor || theme.colors.onPrimary }
]}
>
{confirmText}
</Text>
</TouchableOpacity>
</View>
</Animated.View>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
</Animated.View>
</Modal>
);
};
export default CustomModal;
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'flex-end',
},
keyboardContainer: {
flex: 1,
justifyContent: 'flex-end',
},
modalBox: {
padding: 24,
elevation: 10,
},
title: {
fontSize: responsiveFontSize(2),
fontFamily: FontsFamily.nunitoBold,
marginBottom: 16,
},
content: {
marginBottom: 24,
},
actions: {
flexDirection: 'row',
justifyContent: 'flex-end',
gap: 12,
},
button: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 12,
},
cancelText: {
fontFamily: FontsFamily.nunitoSemiBold,
fontSize: responsiveFontSize(1.6),
},
confirmText: {
fontFamily: FontsFamily.nunitoBold,
fontSize: responsiveFontSize(1.6),
},
});