Yaltopia-Tickets-App/components/ModalToast.tsx
2026-06-05 13:39:37 +03:00

146 lines
3.7 KiB
TypeScript

import React, { useEffect, useRef } from "react";
import { View, StyleSheet, Animated } from "react-native";
import { CheckCircle2, AlertCircle, AlertTriangle, Lightbulb, X } from "@/lib/icons";
import { Text } from "@/components/ui/text";
import { useToast, ToastType } from "@/lib/toast-store";
import { useColorScheme } from "nativewind";
import { useSafeAreaInsets } from "react-native-safe-area-context";
const VARIANT_CONFIG: Record<
ToastType,
{ iconColor: string; borderColor: string; icon: React.ReactNode }
> = {
success: {
iconColor: "#16a34a",
borderColor: "#16a34a",
icon: <CheckCircle2 size={18} color="#16a34a" strokeWidth={2.5} />,
},
error: {
iconColor: "#dc2626",
borderColor: "#dc2626",
icon: <AlertCircle size={18} color="#dc2626" strokeWidth={2.5} />,
},
warning: {
iconColor: "#d97706",
borderColor: "#d97706",
icon: <AlertTriangle size={18} color="#d97706" strokeWidth={2.5} />,
},
info: {
iconColor: "#E46212",
borderColor: "#E46212",
icon: <Lightbulb size={18} color="#E46212" strokeWidth={2.5} />,
},
};
export function ModalToast() {
const { visible, type, title, message, hide, duration } = useToast();
const isDark = useColorScheme() === "dark";
const insets = useSafeAreaInsets();
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();
const timer = setTimeout(hide, duration);
return () => clearTimeout(timer);
}
}, [visible]);
if (!visible) return null;
const config = VARIANT_CONFIG[type];
return (
<View
pointerEvents="box-none"
style={[StyleSheet.absoluteFill, styles.absoluteOverlay]}
>
<Animated.View
style={[
styles.toast,
{
top: insets.top + 12,
backgroundColor: isDark ? "#1C1C1C" : "#ffffff",
borderColor: config.borderColor,
borderWidth: 1,
transform: [{ translateX }],
opacity,
},
]}
>
<View
style={[
styles.iconContainer,
{ backgroundColor: isDark ? "#2a2a2a" : "#f5f5f5" },
]}
>
{config.icon}
</View>
<View style={styles.textContainer}>
<Text className="text-foreground text-[14px] font-sans-black leading-[18px]">
{title}
</Text>
{message ? (
<Text className="text-muted-foreground text-[12px] font-sans-medium leading-[16px] mt-1">
{message}
</Text>
) : null}
</View>
<View className="h-6 w-6 rounded-full items-center justify-center">
<X size={14} color="#9ca3af" strokeWidth={2.5} />
</View>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
absoluteOverlay: {
zIndex: 9999,
elevation: 50,
},
toast: {
marginHorizontal: 16,
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,
},
});