Yaltopia-Tickets-App/components/ModalToast.tsx
2026-06-17 15:16:40 +03:00

138 lines
3.1 KiB
TypeScript

import React, { useEffect, useRef } from "react";
import { View, StyleSheet, Animated } from "react-native";
import {
CheckCircle2,
AlertCircle,
AlertTriangle,
Lightbulb,
} from "@/lib/icons";
import { Text } from "@/components/ui/text";
import { useToast, ToastType } from "@/lib/toast-store";
import { useColorScheme } from "nativewind";
const VARIANT_CONFIG: Record<
ToastType,
{ iconColor: string; icon: typeof CheckCircle2 }
> = {
success: {
iconColor: "#4ADE80",
icon: CheckCircle2,
},
error: {
iconColor: "#F87171",
icon: AlertCircle,
},
warning: {
iconColor: "#FBBF24",
icon: AlertTriangle,
},
info: {
iconColor: "#60A5FA",
icon: Lightbulb,
},
};
export function ModalToast() {
const { visible, type, title, message, hide, duration } = useToast();
const isDark = useColorScheme() === "dark";
const translateY = useRef(new Animated.Value(-20)).current;
const opacity = useRef(new Animated.Value(0)).current;
const hideRef = useRef(hide);
hideRef.current = hide;
useEffect(() => {
if (visible) {
translateY.setValue(-20);
opacity.setValue(0);
Animated.parallel([
Animated.spring(translateY, {
toValue: 0,
useNativeDriver: true,
speed: 20,
bounciness: 6,
}),
Animated.timing(opacity, {
toValue: 1,
duration: 180,
useNativeDriver: true,
}),
]).start();
const timer = setTimeout(() => {
Animated.timing(opacity, {
toValue: 0,
duration: 180,
useNativeDriver: true,
}).start(() => hideRef.current());
}, duration);
return () => clearTimeout(timer);
}
}, [visible, duration]);
if (!visible) return null;
const config = VARIANT_CONFIG[type];
const Icon = config.icon;
return (
<View
pointerEvents="box-none"
style={[StyleSheet.absoluteFill, styles.absoluteOverlay]}
>
<View style={styles.wrapper}>
<Animated.View
style={[
styles.toast,
{
backgroundColor: isDark ? "#1C1C1C" : "#ffffff",
borderColor: isDark ? "#2A2A2A" : "#E5E5E5",
transform: [{ translateY }],
opacity,
},
]}
>
<Icon size={22} color={config.iconColor} strokeWidth={1.5} />
<View style={styles.textContainer}>
<Text className="text-foreground text-[15px] font-sans-semibold tracking-[-0.3px]">
{title}
</Text>
</View>
</Animated.View>
</View>
</View>
);
}
const styles = StyleSheet.create({
absoluteOverlay: {
zIndex: 9999,
elevation: 50,
},
wrapper: {
position: "absolute",
top: 60,
left: 0,
right: 0,
alignItems: "center",
paddingHorizontal: 20,
},
toast: {
width: "100%",
maxWidth: 400,
borderRadius: 14,
paddingHorizontal: 18,
paddingVertical: 16,
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
gap: 12,
},
textContainer: {
flex: 1,
},
});