192 lines
4.4 KiB
TypeScript
192 lines
4.4 KiB
TypeScript
import React, { useEffect, useRef } from "react";
|
|
import { View, Text, StyleSheet, Animated } from "react-native";
|
|
import { CheckCircle2, AlertCircle, Info } from "lucide-react-native";
|
|
|
|
interface ModalToastProps {
|
|
visible: boolean;
|
|
title: string;
|
|
description?: string;
|
|
/**
|
|
* Visual style of the toast.
|
|
* - success: green
|
|
* - error: red
|
|
* - warning: yellow
|
|
* - info: blue
|
|
*/
|
|
variant?: "success" | "error" | "warning" | "info";
|
|
}
|
|
|
|
const ModalToast: React.FC<ModalToastProps> = ({
|
|
visible,
|
|
title,
|
|
description,
|
|
variant = "error",
|
|
}) => {
|
|
const config = getVariantConfig(variant);
|
|
|
|
const Icon = config.icon;
|
|
|
|
const translateX = useRef(new Animated.Value(-40)).current;
|
|
const opacity = useRef(new Animated.Value(0)).current;
|
|
|
|
useEffect(() => {
|
|
if (visible) {
|
|
translateX.setValue(-40);
|
|
opacity.setValue(0);
|
|
|
|
Animated.parallel([
|
|
Animated.spring(translateX, {
|
|
toValue: 0,
|
|
useNativeDriver: true,
|
|
speed: 20,
|
|
bounciness: 6,
|
|
}),
|
|
Animated.timing(opacity, {
|
|
toValue: 1,
|
|
duration: 180,
|
|
useNativeDriver: true,
|
|
}),
|
|
]).start();
|
|
}
|
|
}, [visible, translateX, opacity]);
|
|
|
|
if (!visible) return null;
|
|
|
|
return (
|
|
<View
|
|
pointerEvents="box-none"
|
|
style={[StyleSheet.absoluteFill, styles.absoluteOverlay]}
|
|
>
|
|
<View style={[styles.wrapper, { paddingTop: 0 }]}>
|
|
<Animated.View
|
|
style={[
|
|
styles.toast,
|
|
{
|
|
backgroundColor: config.backgroundColor,
|
|
borderColor: config.borderColor,
|
|
borderWidth: 1,
|
|
transform: [{ translateX }],
|
|
opacity,
|
|
},
|
|
]}
|
|
>
|
|
<View
|
|
style={[
|
|
styles.iconContainer,
|
|
{ backgroundColor: config.iconBackgroundColor },
|
|
]}
|
|
>
|
|
<Icon size={18} color={config.iconColor} />
|
|
</View>
|
|
|
|
<View style={styles.textContainer}>
|
|
<Text style={[styles.title, { color: config.titleColor }]}>
|
|
{title}
|
|
</Text>
|
|
{description ? (
|
|
<Text
|
|
style={[styles.description, { color: config.descriptionColor }]}
|
|
numberOfLines={3}
|
|
>
|
|
{description}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
</Animated.View>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
function getVariantConfig(variant: "success" | "error" | "warning" | "info") {
|
|
switch (variant) {
|
|
case "success":
|
|
return {
|
|
backgroundColor: "#f1f9f5",
|
|
iconBackgroundColor: "#e1f0e2",
|
|
iconColor: "#16a34a",
|
|
titleColor: "#000",
|
|
descriptionColor: "#000",
|
|
icon: CheckCircle2,
|
|
borderColor: "#e1f0e2",
|
|
} as const;
|
|
case "warning":
|
|
return {
|
|
backgroundColor: "#fef7eb",
|
|
iconBackgroundColor: "#ebe3d5",
|
|
iconColor: "#eab308",
|
|
titleColor: "#000",
|
|
descriptionColor: "#000",
|
|
icon: AlertCircle,
|
|
borderColor: "#ebe3d5",
|
|
} as const;
|
|
case "info":
|
|
return {
|
|
backgroundColor: "#e8eefa",
|
|
iconBackgroundColor: "#cdd5e2",
|
|
iconColor: "#2563eb",
|
|
titleColor: "#000",
|
|
descriptionColor: "#000",
|
|
icon: Info,
|
|
borderColor: "#cdd5e2",
|
|
} as const;
|
|
case "error":
|
|
default:
|
|
return {
|
|
backgroundColor: "#fbf0f1",
|
|
iconBackgroundColor: "#ebd8d4",
|
|
iconColor: "#dc2000",
|
|
titleColor: "#000000",
|
|
descriptionColor: "#000",
|
|
icon: AlertCircle,
|
|
borderColor: "#ebd8d4",
|
|
} as const;
|
|
}
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
absoluteOverlay: {
|
|
zIndex: 9999,
|
|
elevation: 50,
|
|
},
|
|
wrapper: {
|
|
alignItems: "center",
|
|
paddingHorizontal: 16,
|
|
marginTop: 16,
|
|
},
|
|
toast: {
|
|
width: "100%",
|
|
maxWidth: 360,
|
|
borderRadius: 12,
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
shadowColor: "#000",
|
|
shadowOpacity: 0.18,
|
|
shadowRadius: 8,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
},
|
|
iconContainer: {
|
|
width: 32,
|
|
height: 32,
|
|
borderRadius: 16,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
},
|
|
textContainer: {
|
|
flex: 1,
|
|
marginLeft: 12,
|
|
},
|
|
title: {
|
|
fontSize: 14,
|
|
fontWeight: "700",
|
|
},
|
|
description: {
|
|
fontSize: 12,
|
|
marginTop: 4,
|
|
},
|
|
});
|
|
|
|
export default ModalToast;
|