UNPKG

@naarni/design-system

Version:

Naarni React Native Design System for EV Fleet Apps

147 lines (146 loc) 5.28 kB
import React from 'react'; import { View, TouchableOpacity, StyleSheet, Animated } from 'react-native'; import { Text } from '../Text/Text'; import { useDeviceTheme } from '../../theme/deviceTheme'; import { Icon } from '../Icon/Icon'; import { Button } from '../Button/Button'; // Force cache refresh - using device theme export const Alert = ({ variant = 'info', title, message, dismissible = false, persistent = false, onDismiss, icon, style, actions, }) => { const { isDark } = useDeviceTheme(); const [isVisible, setIsVisible] = React.useState(true); const fadeAnim = React.useRef(new Animated.Value(1)).current; const getVariantStyles = () => { const baseStyles = { info: { background: isDark ? 'rgba(0, 122, 255, 0.15)' : 'rgba(0, 122, 255, 0.1)', border: isDark ? 'rgba(0, 122, 255, 0.3)' : 'rgba(0, 122, 255, 0.2)', icon: isDark ? '#4DA3FF' : '#007AFF', text: isDark ? '#E5F1FF' : '#003366', accent: '#007AFF', }, success: { background: isDark ? 'rgba(52, 199, 89, 0.15)' : 'rgba(52, 199, 89, 0.1)', border: isDark ? 'rgba(52, 199, 89, 0.3)' : 'rgba(52, 199, 89, 0.2)', icon: isDark ? '#5CD679' : '#34C759', text: isDark ? '#E5FFED' : '#1A4D2E', accent: '#34C759', }, warning: { background: isDark ? 'rgba(255, 149, 0, 0.15)' : 'rgba(255, 149, 0, 0.1)', border: isDark ? 'rgba(255, 149, 0, 0.3)' : 'rgba(255, 149, 0, 0.2)', icon: isDark ? '#FFAA33' : '#FF9500', text: isDark ? '#FFF4E5' : '#663500', accent: '#FF9500', }, error: { background: isDark ? 'rgba(255, 59, 48, 0.15)' : 'rgba(255, 59, 48, 0.1)', border: isDark ? 'rgba(255, 59, 48, 0.3)' : 'rgba(255, 59, 48, 0.2)', icon: isDark ? '#FF6961' : '#FF3B30', text: isDark ? '#FFE5E5' : '#660000', accent: '#FF3B30', }, }; return baseStyles[variant]; }; const getVariantIcon = () => { const iconMap = { info: 'information', success: 'check-circle', warning: 'alert-circle', error: 'close-circle', }; return icon || <Icon name={iconMap[variant]} size={24} color={getVariantStyles().icon}/>; }; const handleDismiss = () => { if (!persistent) { Animated.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: true, }).start(() => { setIsVisible(false); onDismiss?.(); }); } }; if (!isVisible) return null; const styles = getVariantStyles(); return (<Animated.View style={[ { opacity: fadeAnim, backgroundColor: styles.background, borderColor: styles.border, }, componentStyles.container, style, ]}> <View style={componentStyles.content}> <View style={[componentStyles.iconContainer, { backgroundColor: styles.accent + '20' }]}> {getVariantIcon()} </View> <View style={componentStyles.textContainer}> {title && (<Text variant="subtitle1" style={{ ...componentStyles.title, color: styles.text }}> {title} </Text>)} <Text variant="body2" style={{ ...componentStyles.message, color: styles.text }}> {message} </Text> {actions && actions.length > 0 && (<View style={componentStyles.actionsContainer}> {actions.map((action, index) => (<Button key={index} variant={action.variant || 'text'} size="small" onPress={action.onPress} style={componentStyles.actionButton}> {action.label} </Button>))} </View>)} </View> {dismissible && (<TouchableOpacity onPress={handleDismiss} style={[componentStyles.dismissButton, { backgroundColor: styles.accent + '20' }]}> <Icon name="close" size={16} color={styles.accent}/> </TouchableOpacity>)} </View> </Animated.View>); }; const componentStyles = StyleSheet.create({ container: { borderRadius: 12, borderWidth: 1, marginVertical: 8, overflow: 'hidden', }, content: { flexDirection: 'row', padding: 16, alignItems: 'flex-start', }, iconContainer: { width: 24, height: 24, borderRadius: 12, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, textContainer: { flex: 1, }, title: { marginBottom: 4, }, message: { lineHeight: 20, }, actionsContainer: { flexDirection: 'row', marginTop: 12, gap: 8, }, actionButton: { minWidth: 80, }, dismissButton: { width: 24, height: 24, borderRadius: 12, justifyContent: 'center', alignItems: 'center', marginLeft: 8, }, });