@naarni/design-system
Version:
Naarni React Native Design System for EV Fleet Apps
147 lines (146 loc) • 5.28 kB
JavaScript
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,
},
});