UNPKG

react-native-lq-toast

Version:
138 lines (137 loc) 5.48 kB
import React, { useEffect, useRef, useState } from "react"; import { Animated, StyleSheet, View, TouchableOpacity, Text, Keyboard, } from "react-native"; const iconsMap = { success: <Text></Text>, error: <Text></Text>, warning: <Text>⚠️</Text>, default: <Text></Text>, }; const LQToast = ({ title, description, variant = "default", isVisible, position = "top", duration, offsetTop = 60, offsetBottom = 100, animationType = "slide", onDismiss, customToastComponent: CustomComponent, }) => { const slideAnim = useRef(new Animated.Value(0)).current; const fadeAnim = useRef(new Animated.Value(0)).current; const [keyboardHeight, setKeyboardHeight] = useState(0); const [toastHeight, setToastHeight] = useState(0); // State to store toast height useEffect(() => { if (position === "bottom") { const keyboardDidShowListener = Keyboard.addListener("keyboardDidShow", (event) => setKeyboardHeight(event.endCoordinates.height)); const keyboardDidHideListener = Keyboard.addListener("keyboardDidHide", () => setKeyboardHeight(0)); return () => { keyboardDidShowListener.remove(); keyboardDidHideListener.remove(); }; } }, [position]); useEffect(() => { slideAnim.setValue(0); fadeAnim.setValue(0); }, [animationType, position]); useEffect(() => { if (position === "center" || animationType === "fade") { Animated.timing(fadeAnim, { toValue: isVisible ? 1 : 0, duration, useNativeDriver: true, }).start(); } else { const initialTranslateY = position === "top" ? -offsetTop - toastHeight : offsetBottom + toastHeight; slideAnim.setValue(initialTranslateY); Animated.timing(slideAnim, { toValue: isVisible ? position === "top" ? offsetTop : -offsetBottom : initialTranslateY, duration, useNativeDriver: true, }).start(); } }, [isVisible, position, offsetTop, offsetBottom, toastHeight]); const handleLayout = (event) => { const { height } = event.nativeEvent.layout; setToastHeight(height); }; if (CustomComponent) { return (<Animated.View onLayout={handleLayout} style={[ styles.toastContainer, position === "center" && styles.centerContainer, { transform: [ position === "center" || animationType === "fade" ? { translateY: 0 } : { translateY: slideAnim }, ], opacity: position === "center" || animationType === "fade" ? fadeAnim : 1, [position]: position === "bottom" ? keyboardHeight > 0 ? keyboardHeight + offsetBottom : offsetBottom : position === "top" ? offsetTop : undefined, }, ]}> <CustomComponent onDismiss={onDismiss}/> </Animated.View>); } return (<Animated.View onLayout={handleLayout} style={[ styles.toastContainer, styles[variant], position === "center" && styles.centerContainer, { transform: [ position === "center" || animationType === "fade" ? { translateY: 0 } : { translateY: slideAnim }, ], opacity: position === "center" || animationType === "fade" ? fadeAnim : 1, [position]: position === "bottom" ? keyboardHeight > 0 ? keyboardHeight + offsetBottom : offsetBottom : position === "top" ? offsetTop : undefined, }, ]}> {iconsMap[variant]} <View style={{ flex: 1, gap: 3 }}> {title && <Text style={styles.title}>{title}</Text>} {description && (<Text style={styles.description}>{description}</Text>)} </View> <TouchableOpacity style={{ marginRight: 3 }} onPress={onDismiss}> <Text></Text> </TouchableOpacity> </Animated.View>); }; export default LQToast; const styles = StyleSheet.create({ toastContainer: { position: "absolute", paddingHorizontal: 10, paddingVertical: 13, right: 15, left: 15, borderRadius: 5, zIndex: 9999, flexDirection: "row", gap: 10, }, centerContainer: { top: "50%", marginTop: -25, alignSelf: "center", }, success: { backgroundColor: "#EFFAF6" }, error: { backgroundColor: "#FDEDF0" }, warning: { backgroundColor: "#FFF3CD" }, default: { backgroundColor: "#fff" }, title: { fontSize: 15, fontWeight: "700", color: "#0A0D14" }, description: { color: "#64748B", fontSize: 15, fontWeight: "500" }, });