Greenlens/components/Toast.tsx

80 lines
2.2 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { Animated, Text, StyleSheet, View } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useColors } from '../constants/Colors';
import { ColorPalette } from '../types';
interface ToastProps {
message: string;
isVisible: boolean;
onClose: () => void;
isDark?: boolean;
colorPalette?: ColorPalette;
}
export const Toast: React.FC<ToastProps> = ({
message,
isVisible,
onClose,
isDark = false,
colorPalette = 'forest',
}) => {
const colors = useColors(isDark, colorPalette);
const opacity = useRef(new Animated.Value(0)).current;
const translateY = useRef(new Animated.Value(20)).current;
useEffect(() => {
if (isVisible) {
Animated.parallel([
Animated.timing(opacity, { toValue: 1, duration: 300, useNativeDriver: true }),
Animated.timing(translateY, { toValue: 0, duration: 300, useNativeDriver: true }),
]).start();
const timer = setTimeout(() => {
Animated.parallel([
Animated.timing(opacity, { toValue: 0, duration: 300, useNativeDriver: true }),
Animated.timing(translateY, { toValue: 20, duration: 300, useNativeDriver: true }),
]).start(() => onClose());
}, 3000);
return () => clearTimeout(timer);
}
}, [isVisible]);
if (!isVisible) return null;
return (
<Animated.View style={[styles.container, { opacity, transform: [{ translateY }] }]}>
<View style={[styles.toast, { backgroundColor: colors.surface, shadowColor: colors.overlayStrong }]}>
<Ionicons name="checkmark-circle" size={18} color={colors.success} />
<Text style={[styles.text, { color: colors.text }]}>{message}</Text>
</View>
</Animated.View>
);
};
const styles = StyleSheet.create({
container: {
position: 'absolute',
bottom: 100,
left: 0,
right: 0,
alignItems: 'center',
zIndex: 70,
pointerEvents: 'none',
},
toast: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 24,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 8,
},
text: { fontSize: 13, fontWeight: '500' },
});