expo-react-native-toastify
Version:
expo-react-native-toastify allows you to add notifications to your expo react-native app (ios, android, web) with ease.
677 lines (647 loc) • 20.1 kB
JavaScript
import React, { useState } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
ScrollView,
TextInput,
Linking,
Alert,
Platform
} from "react-native";
import ToastManager, { Toast } from "./lib";
const POSITIONS = [
{ label: "Top", value: "top" },
{ label: "Center", value: "center" },
{ label: "Bottom", value: "bottom" }
];
const DURATIONS = [
{ label: "2s", value: 2000 },
{ label: "3s", value: 3000 },
{ label: "5s", value: 5000 },
{ label: "Static", value: 0 },
{ label: "Custom", value: 'custom' }
];
const ICONS = [
{ label: "None", value: null },
{ label: "Info", value: "information-circle" },
{ label: "Heart", value: "heart" },
{ label: "Star", value: "star" },
{ label: "Alert", value: "alert-circle" },
{ label: "Check", value: "checkmark-circle" },
{ label: "Warning", value: "warning" }
];
const TOAST_TYPES = [
{ label: "Info", type: "info", color: "#3498db" },
{ label: "Success", type: "success", color: "#07bc0c" },
{ label: "Warning", type: "warning", color: "#f1c40f" },
{ label: "Error", type: "error", color: "#e74c3c" },
{ label: "Custom Style", type: "custom", color: "#8B5CF6" }
];
const CustomContent = ({ type, color, isDark }) => (
<View style={styles.customToastContent}>
<View style={[styles.customBadge, { backgroundColor: color }]}>
<Text style={styles.customBadgeText}>{type.toUpperCase()}</Text>
</View>
<Text style={[styles.customTitle, isDark && styles.darkCustomText]}>Custom Toast</Text>
<TouchableOpacity style={[styles.customButton]} onPress={() => Platform.OS === 'web' ? console.log('pressed') : Alert.alert("expo-react-native-toastify", "expo-react-native-toastify-demo", [{ text: "Close", onPress: () => console.log("OK Pressed") }])}>
<Text style={{ color: color }}>Press Me</Text>
</TouchableOpacity>
</View>
);
const getDefaultMessage = (type) => {
switch (type) {
case 'info': return "This is an info message";
case 'success': return "Operation completed successfully";
case 'warning': return "Please proceed with caution";
case 'error': return "An error has occurred";
case 'custom': return "Custom styled toast";
default: return "Notification";
}
};
const Demo = () => {
const [position, setPosition] = useState("top");
const [duration, setDuration] = useState(3000);
const [customDuration, setCustomDuration] = useState("");
const [isDark, setIsDark] = useState(false);
const [selectedIcon, setSelectedIcon] = useState(null);
const [useComponent, setUseComponent] = useState(false);
const handleDurationChange = (value) => {
if (value === 'custom') {
setDuration(null);
} else {
setDuration(value);
setCustomDuration("");
}
};
const showToast = (type, color) => {
const baseOptions = {
position,
duration: duration !== null? duration:(customDuration ? parseInt(customDuration) * 1000 : 3000),
theme: isDark ? 'dark' : 'light',
};
const content = useComponent
? <CustomContent type={type} color={color} isDark={isDark} />
: getDefaultMessage(type);
// Only add width-related options when using component
const options = useComponent ? {
...baseOptions,
width: 350,
modalWidth: 400,
height: 80,
} : baseOptions;
if (type === 'custom') {
Toast.custom(content, {
...options,
icon: selectedIcon,
barColor: color,
style: {
borderRadius: 12,
borderLeftWidth: 4,
borderLeftColor: color,
}
});
} else {
switch (type) {
case 'info':
Toast.info(content, options);
break;
case 'success':
Toast.success(content, options);
break;
case 'warning':
Toast.warn(content, options);
break;
case 'error':
Toast.error(content, options);
break;
}
}
};
const SelectionGroup = ({ title, items, selected, onSelect, colorKey }) => (
<View style={styles.section}>
<Text style={[styles.sectionTitle, isDark && styles.darkText]}>{title}</Text>
<View style={styles.optionsGrid}>
{items.map((item) => (
<TouchableOpacity
key={item.value || item.type}
style={[
styles.optionButton,
selected === (item.value || item.type) && styles.selectedOption,
isDark && styles.darkOption,
colorKey && { borderColor: item[colorKey] },
isDark && selected === (item.value || item.type) && styles.darkSelectedOption,
]}
onPress={() => onSelect(item.value || item.type)}
>
<Text style={[
styles.optionText,
selected === (item.value || item.type) && styles.selectedOptionText,
isDark && styles.darkText,
colorKey && { color: item[colorKey] },
isDark && selected === (item.value || item.type) && styles.darkSelectedOptionText,
]}>
{item.label}
</Text>
</TouchableOpacity>
))}
</View>
</View>
);
return (
<ScrollView style={[styles.container, isDark && styles.darkContainer]}>
<View style={styles.docsSection}>
<Text style={[styles.mainTitle, isDark && styles.darkText, {
textAlign: 'center',
}]}>
expo-react-native-toastify
</Text>
<Text style={[styles.sectionTitle, isDark && styles.darkText, { textAlign: 'center' }]}>
🌐 Web | 🍎 iOS | ▶ Android
</Text>
<Text style={[styles.description, isDark && styles.darkText]}>
Easy notifications to your expo react-native app
</Text>
</View>
<ToastManager />
<View style={styles.header}>
<View style={styles.toggleGroup}>
<Text style={[styles.toggleLabel, isDark && styles.darkText]}>Theme:</Text>
<View style={styles.toggleButtons}>
<TouchableOpacity
style={[
styles.toggleButton,
!isDark && styles.selectedToggle,
isDark && styles.darkToggleButton,
isDark && !isDark && styles.darkSelectedToggle
]}
onPress={() => setIsDark(false)}
>
<Text style={[
styles.toggleButtonText,
!isDark && styles.selectedToggleText,
isDark && styles.darkText,
isDark && !isDark && styles.darkSelectedToggleText
]}>
☀️ Light
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.toggleButton,
isDark && styles.selectedToggle,
isDark && styles.darkToggleButton,
isDark && isDark && styles.darkSelectedToggle
]}
onPress={() => setIsDark(true)}
>
<Text style={[
styles.toggleButtonText,
isDark && styles.selectedToggleText,
isDark && styles.darkText,
isDark && isDark && styles.darkSelectedToggleText
]}>
🌙 Dark
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.toggleGroup}>
<Text style={[styles.toggleLabel, isDark && styles.darkText]}>Content:</Text>
<View style={styles.toggleButtons}>
<TouchableOpacity
style={[
styles.toggleButton,
!useComponent && styles.selectedToggle,
isDark && styles.darkToggleButton,
isDark && !useComponent && styles.darkSelectedToggle
]}
onPress={() => setUseComponent(false)}
>
<Text style={[
styles.toggleButtonText,
!useComponent && styles.selectedToggleText,
isDark && styles.darkText,
isDark && !useComponent && styles.darkSelectedToggleText
]}>
📝 Text
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.toggleButton,
useComponent && styles.selectedToggle,
isDark && styles.darkToggleButton,
isDark && useComponent && styles.darkSelectedToggle
]}
onPress={() => setUseComponent(true)}
>
<Text style={[
styles.toggleButtonText,
useComponent && styles.selectedToggleText,
isDark && styles.darkText,
isDark && useComponent && styles.darkSelectedToggleText
]}>
📱 Component
</Text>
</TouchableOpacity>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={[styles.sectionTitle, isDark && styles.darkText]}>Click to Toast</Text>
<View style={styles.toastTypeGrid}>
{TOAST_TYPES.map((toast) => (
<TouchableOpacity
key={toast.type}
style={[styles.toastTypeButton, { backgroundColor: toast.color }]}
onPress={() => showToast(toast.type, toast.color)}
>
<Text style={styles.toastTypeText}>{toast.label}</Text>
</TouchableOpacity>
))}
</View>
</View>
<SelectionGroup
title="Position"
items={POSITIONS}
selected={position}
onSelect={setPosition}
/>
<View style={styles.section}>
<Text style={[styles.sectionTitle, isDark && styles.darkText]}>Duration</Text>
<View style={styles.optionsGrid}>
{DURATIONS.map((item) => (
<TouchableOpacity
key={item.value}
style={[
styles.optionButton,
duration === item.value && styles.selectedOption,
isDark && styles.darkOption,
isDark && duration === item.value && styles.darkSelectedOption,
]}
onPress={() => handleDurationChange(item.value)}
>
<Text style={[
styles.optionText,
duration === item.value && styles.selectedOptionText,
isDark && styles.darkText,
isDark && duration === item.value && styles.darkSelectedOptionText,
]}>
{item.label}
</Text>
</TouchableOpacity>
))}
</View>
{duration === null && (
<TextInput
style={[styles.customDurationInput, isDark && styles.darkInput]}
placeholder="Enter duration in seconds"
placeholderTextColor={isDark ? '#9CA3AF' : '#6B7280'}
keyboardType="numeric"
value={customDuration}
onChangeText={setCustomDuration}
/>
)}
</View>
{TOAST_TYPES.find(t => t.type === 'custom')?.type === 'custom' && (
<View style={styles.section}>
<Text style={[styles.sectionTitle, isDark && styles.darkText]}>Icon (Available with Text and Custom Style Toast)</Text>
<TouchableOpacity onPress={() => Linking.openURL('https://oblador.github.io/react-native-vector-icons/')}>
<Text style={[styles.iconNote, isDark && styles.darkText, { textDecorationLine: 'underline', color: 'blue' }]}>
Use any Icons available from (react-native-vector-icons)
</Text>
</TouchableOpacity>
<View style={styles.optionsGrid}>
{ICONS.map((item) => (
<TouchableOpacity
key={item.value || item.type}
style={[
styles.optionButton,
selectedIcon === (item.value || item.type) && styles.selectedOption,
isDark && styles.darkOption,
isDark && selectedIcon === (item.value || item.type) && styles.darkSelectedOption,
]}
onPress={() => setSelectedIcon(item.value || item.type)}
>
<Text style={[
styles.optionText,
selectedIcon === (item.value || item.type) && styles.selectedOptionText,
isDark && styles.darkText,
isDark && selectedIcon === (item.value || item.type) && styles.darkSelectedOptionText,
]}>
{item.label}
</Text>
</TouchableOpacity>
))}
</View>
</View>
)}
<View style={styles.docsSection}>
<View style={styles.installSection}>
<Text style={[styles.installTitle, isDark && styles.darkText]}>Installation</Text>
<View style={[styles.codeBlock, isDark && styles.darkCodeBlock]}>
<Text style={styles.codeText}>npm install expo-react-native-toastify</Text>
</View>
<Text style={[styles.orText, isDark && styles.darkText]}>or</Text>
<View style={[styles.codeBlock, isDark && styles.darkCodeBlock]}>
<Text style={styles.codeText}>yarn add expo-react-native-toastify</Text>
</View>
</View>
<View style={styles.linksSection}>
<TouchableOpacity
onPress={() => Linking.openURL('https://www.npmjs.com/package/expo-react-native-toastify')}
style={styles.link}
>
<Text style={[styles.linkText, isDark && styles.darkLinkText]}>
📦 Documentation
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => Linking.openURL('https://oblador.github.io/react-native-vector-icons/')}
style={styles.link}
>
<Text style={[styles.linkText, isDark && styles.darkLinkText]}>
🎨 Available Icons
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.footer}>
<TouchableOpacity
onPress={() => Linking.openURL('https://www.npmjs.com/package/expo-react-native-toastify')}
style={styles.npmLink}
>
<Text style={[styles.footerText, isDark && styles.darkText]}>
expo-react-native-toastify
</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: '#f8fafc'
},
darkContainer: {
backgroundColor: '#1a1a1a'
},
section: {
marginBottom: 24,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 12,
color: '#1F2937'
},
darkText: {
color: '#f8fafc'
},
optionsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
justifyContent: 'flex-start',
},
optionButton: {
paddingVertical: 8,
paddingHorizontal: 16,
borderRadius: 8,
borderWidth: 1,
borderColor: '#e2e8f0',
backgroundColor: '#ffffff',
alignItems: 'center',
justifyContent: 'center',
flexGrow: 0,
flexShrink: 0,
},
darkOption: {
backgroundColor: '#2d2d2d',
borderColor: '#404040'
},
selectedOption: {
borderColor: '#8B5CF6',
backgroundColor: '#F5F3FF'
},
optionText: {
fontSize: 14,
color: '#4B5563',
fontWeight: '500'
},
selectedOptionText: {
color: '#8B5CF6',
fontWeight: '600'
},
darkSelectedOption: {
borderColor: '#8B5CF6',
backgroundColor: '#4C1D95',
},
darkSelectedOptionText: {
color: '#ffffff',
fontWeight: '600'
},
header: {
marginBottom: 24,
gap: 16,
},
toggleGroup: {
gap: 8,
},
toggleLabel: {
fontSize: 14,
fontWeight: '500',
color: '#4B5563',
},
toggleButtons: {
flexDirection: 'row',
gap: 8,
},
toggleButton: {
flex: 1,
padding: 12,
borderRadius: 8,
borderWidth: 1,
borderColor: '#e2e8f0',
backgroundColor: '#ffffff',
alignItems: 'center',
},
darkToggleButton: {
backgroundColor: '#2d2d2d',
borderColor: '#404040',
},
selectedToggle: {
borderColor: '#8B5CF6',
backgroundColor: '#F5F3FF',
},
toggleButtonText: {
fontSize: 14,
fontWeight: '500',
color: '#4B5563',
},
selectedToggleText: {
color: '#8B5CF6',
fontWeight: '600',
},
toastTypeGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
justifyContent: 'space-between',
},
toastTypeButton: {
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 8,
alignItems: 'center',
},
toastTypeText: {
color: '#ffffff',
fontSize: 14,
fontWeight: '600',
},
darkSelectedToggle: {
borderColor: '#8B5CF6',
backgroundColor: '#4C1D95',
},
darkSelectedToggleText: {
color: '#ffffff',
},
customDurationInput: {
marginTop: 8,
padding: 12,
borderWidth: 1,
borderColor: '#E5E7EB',
borderRadius: 8,
fontSize: 16,
color: '#1F2937',
backgroundColor: '#ffffff',
},
darkInput: {
borderColor: '#404040',
backgroundColor: '#2d2d2d',
color: '#ffffff',
},
customToastContent: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 4,
paddingRight: 8,
gap: 8,
},
customBadge: {
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 4,
},
customBadgeText: {
color: '#ffffff',
fontSize: 10,
fontWeight: '600',
},
customTitle: {
fontSize: 14,
fontWeight: '600',
color: '#1F2937',
flex: 1,
},
customButton: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 4,
backgroundColor: '#F3F4F6',
},
darkCustomText: {
color: '#ffffff',
},
iconNote: {
fontSize: 12,
color: '#6B7280',
marginTop: -8,
marginBottom: 12,
fontStyle: 'italic',
},
footer: {
paddingVertical: 24,
alignItems: 'center',
},
npmLink: {
padding: 8,
},
footerText: {
fontSize: 12,
color: '#3B82F6',
textDecorationLine: 'underline',
},
mainTitle: {
fontSize: 24,
fontWeight: '700',
color: '#1F2937',
textAlign: 'center',
marginBottom: 8,
},
description: {
fontSize: 16,
color: '#4B5563',
textAlign: 'center',
marginBottom: 24,
},
installSection: {
marginBottom: 24,
alignItems: 'center',
},
installTitle: {
fontSize: 18,
fontWeight: '600',
color: '#1F2937',
marginBottom: 12,
},
codeBlock: {
backgroundColor: '#F1F5F9',
padding: 12,
borderRadius: 8,
width: '100%',
marginBottom: 8,
},
darkCodeBlock: {
backgroundColor: '#374151',
},
codeText: {
fontFamily: 'monospace',
fontSize: 14,
color: '#6366F1',
},
orText: {
fontSize: 14,
color: '#6B7280',
marginVertical: 4,
},
linksSection: {
flexDirection: 'row',
justifyContent: 'center',
gap: 16,
marginBottom: 24,
},
link: {
padding: 8,
},
linkText: {
fontSize: 14,
color: '#3B82F6',
textDecorationLine: 'underline',
},
darkLinkText: {
color: '#60A5FA',
},
docsSection: {
marginTop: 32,
paddingTop: 32,
borderTopWidth: 1,
borderTopColor: '#E5E7EB',
},
});
export default Demo;