107 lines
2.5 KiB
TypeScript
107 lines
2.5 KiB
TypeScript
import React, { createContext, useContext, useState, useRef } from "react";
|
|
import { Animated, Pressable, Text, View, StyleSheet } from "react-native";
|
|
import { Portal } from "@gorhom/portal";
|
|
|
|
const ToastContext = createContext(null);
|
|
|
|
export const useToast = () => useContext(ToastContext);
|
|
|
|
export const ToastProvider = ({ children }) => {
|
|
const [toast, setToast] = useState(null);
|
|
const opacity = useRef(new Animated.Value(0)).current;
|
|
const translateY = useRef(new Animated.Value(20)).current;
|
|
|
|
const show = (options) => {
|
|
setToast(options);
|
|
|
|
Animated.parallel([
|
|
Animated.timing(opacity, {
|
|
toValue: 1,
|
|
duration: 200,
|
|
useNativeDriver: true,
|
|
}),
|
|
Animated.timing(translateY, {
|
|
toValue: 0,
|
|
duration: 200,
|
|
useNativeDriver: true,
|
|
}),
|
|
]).start();
|
|
|
|
setTimeout(() => hide(), options.duration || 3000);
|
|
};
|
|
|
|
const hide = () => {
|
|
Animated.parallel([
|
|
Animated.timing(opacity, {
|
|
toValue: 0,
|
|
duration: 200,
|
|
useNativeDriver: true,
|
|
}),
|
|
Animated.timing(translateY, {
|
|
toValue: 20,
|
|
duration: 200,
|
|
useNativeDriver: true,
|
|
}),
|
|
]).start(() => setToast(null));
|
|
};
|
|
|
|
return (
|
|
<ToastContext.Provider value={{ show }}>
|
|
{children}
|
|
|
|
<Portal>
|
|
{toast && (
|
|
<Animated.View
|
|
style={[
|
|
styles.toastContainer,
|
|
{
|
|
opacity,
|
|
transform: [{ translateY }],
|
|
borderLeftColor:
|
|
toast.variant === "destructive" ? "#ef4444" : "#4ade80",
|
|
},
|
|
]}
|
|
>
|
|
<Pressable onPress={hide} style={styles.toast}>
|
|
<Text style={styles.title}>{toast.title}</Text>
|
|
{toast.description && (
|
|
<Text style={styles.description}>{toast.description}</Text>
|
|
)}
|
|
</Pressable>
|
|
</Animated.View>
|
|
)}
|
|
</Portal>
|
|
</ToastContext.Provider>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
toastContainer: {
|
|
position: "absolute",
|
|
top: 50,
|
|
left: 20,
|
|
right: 20,
|
|
zIndex: 9999,
|
|
elevation: 9999,
|
|
shadowOpacity: 0.2,
|
|
shadowRadius: 8,
|
|
shadowOffset: { width: 0, height: 4 },
|
|
},
|
|
toast: {
|
|
backgroundColor: "rgba(30, 30, 30, 0.95)",
|
|
borderRadius: 14,
|
|
padding: 14,
|
|
borderLeftWidth: 4,
|
|
},
|
|
title: {
|
|
color: "white",
|
|
fontSize: 16,
|
|
fontWeight: "600",
|
|
},
|
|
description: {
|
|
color: "#d4d4d4",
|
|
fontSize: 14,
|
|
marginTop: 2,
|
|
},
|
|
});
|