UNPKG

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
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;