UNPKG

@naarni/design-system

Version:

Naarni React Native Design System for EV Fleet Apps

214 lines (213 loc) 8.79 kB
import React, { useEffect, useRef } from 'react'; import { View, Image, Text, Animated, Easing } from 'react-native'; import { useCardStyles } from './styles'; import { useDeviceTheme } from '../../theme/deviceTheme'; // Helper: Shine Animation Overlay const ShineOverlay = ({ loop }) => { const shinyAnimation = useRef(new Animated.Value(0)).current; const { colors, isDark } = useDeviceTheme(); useEffect(() => { console.log('ShineOverlay: Starting animation, loop:', loop); const animate = () => { shinyAnimation.setValue(0); Animated.timing(shinyAnimation, { toValue: 1, duration: 1500, useNativeDriver: true, easing: Easing.linear, }).start(({ finished }) => { if (loop && finished) animate(); }); }; animate(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loop]); const shinyTranslateX = shinyAnimation.interpolate({ inputRange: [0, 1], outputRange: [-300, 300], }); const shinyOpacity = shinyAnimation.interpolate({ inputRange: [0, 0.3, 0.7, 1], outputRange: [0, 0.8, 0.8, 0], }); // Use attractive gradient colors based on theme const shineColor = isDark ? 'rgba(139, 92, 246, 0.4)' // Purple for dark theme : 'rgba(99, 102, 241, 0.4)'; // Indigo for light theme return (<Animated.View pointerEvents="none" style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: shineColor, transform: [{ translateX: shinyTranslateX }], opacity: shinyOpacity, zIndex: 1, }}/>); }; // Helper: Sine Wave Shine Animation const SineShineOverlay = ({ loop }) => { const anim = useRef(new Animated.Value(0)).current; const { colors, isDark } = useDeviceTheme(); useEffect(() => { console.log('SineShineOverlay: Starting animation, loop:', loop); const animate = () => { anim.setValue(0); Animated.timing(anim, { toValue: 1, duration: 2000, useNativeDriver: true, easing: Easing.linear, }).start(({ finished }) => { if (loop && finished) animate(); }); }; animate(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loop]); const translateX = anim.interpolate({ inputRange: [0, 1], outputRange: [-300, 300] }); // Use attractive gradient colors const sineColor = isDark ? 'rgba(6, 182, 212, 0.7)' // Cyan for dark theme : 'rgba(16, 185, 129, 0.7)'; // Emerald for light theme return (<Animated.View pointerEvents="none" style={{ position: 'absolute', left: 0, right: 0, bottom: 32, height: 32, opacity: 0.9, zIndex: 1, transform: [{ translateX }], }}> {/* Sine wave band with attractive color */} <View style={{ flex: 1, backgroundColor: sineColor, borderRadius: 16, shadowColor: isDark ? '#06B6D4' : '#10B981', shadowOpacity: 0.8, shadowRadius: 8, }}/> </Animated.View>); }; // Helper: Stars Animation const StarsOverlay = ({ loop }) => { const { colors, isDark } = useDeviceTheme(); // For simplicity, use a static set of twinkling stars with opacity animation const starOpacities = [useRef(new Animated.Value(1)).current, useRef(new Animated.Value(1)).current, useRef(new Animated.Value(1)).current]; useEffect(() => { console.log('StarsOverlay: Starting animation, loop:', loop); starOpacities.forEach((opacity, i) => { const animate = () => { Animated.sequence([ Animated.timing(opacity, { toValue: 0.2, duration: 400 + i * 100, useNativeDriver: true }), Animated.timing(opacity, { toValue: 1, duration: 400 + i * 100, useNativeDriver: true }), ]).start(() => { if (loop) animate(); }); }; animate(); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loop]); // Use attractive star colors const starColors = isDark ? ['#FBBF24', '#F472B6', '#2DD4BF'] // Amber, Pink, Teal for dark theme : ['#F59E0B', '#EC4899', '#14B8A6']; // Amber, Pink, Teal for light theme return (<View pointerEvents="none" style={{ position: 'absolute', left: 0, right: 0, bottom: 8, height: 24, flexDirection: 'row', justifyContent: 'center', zIndex: 1 }}> {[0, 1, 2].map(i => (<Animated.View key={i} style={{ opacity: starOpacities[i], marginHorizontal: 8 }}> <View style={{ width: 12, height: 12, borderRadius: 6, backgroundColor: starColors[i], shadowColor: starColors[i], shadowOpacity: 1, shadowRadius: 8, elevation: 4, }}/> </Animated.View>))} </View>); }; // Helper: Drops Animation const DropsOverlay = ({ loop }) => { const { colors, isDark } = useDeviceTheme(); // Three animated drops falling const dropYs = [useRef(new Animated.Value(0)).current, useRef(new Animated.Value(0)).current, useRef(new Animated.Value(0)).current]; useEffect(() => { console.log('DropsOverlay: Starting animation, loop:', loop); dropYs.forEach((dropY, i) => { const animate = () => { dropY.setValue(0); Animated.timing(dropY, { toValue: 1, duration: 1200 + i * 200, useNativeDriver: true, easing: Easing.in(Easing.quad), }).start(() => { if (loop) animate(); }); }; animate(); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loop]); // Use attractive drop colors const dropColors = isDark ? ['#F87171', '#60A5FA', '#A78BFA'] // Red, Blue, Purple for dark theme : ['#EF4444', '#3B82F6', '#8B5CF6']; // Red, Blue, Purple for light theme return (<View pointerEvents="none" style={{ position: 'absolute', left: 0, right: 0, bottom: 8, height: 24, flexDirection: 'row', justifyContent: 'center', zIndex: 1 }}> {[0, 1, 2].map(i => (<Animated.View key={i} style={{ transform: [{ translateY: dropYs[i].interpolate({ inputRange: [0, 1], outputRange: [0, 16] }) }], marginHorizontal: 12, }}> <View style={{ width: 8, height: 16, borderRadius: 4, backgroundColor: dropColors[i], shadowColor: dropColors[i], shadowOpacity: 0.7, shadowRadius: 4, }}/> </Animated.View>))} </View>); }; /** * Card component for Naarni Design System * * @param {CardProps} props - Card properties * @returns {JSX.Element} */ export const Card = ({ title, subtitle, image, elevation = 0, style, children, backgroundColor, key, animated = true, animationType = 'shine', loop = false, }) => { const { colors } = useDeviceTheme(); const { getCardStyle, getTitleStyle, getSubtitleStyle, getImageStyle } = useCardStyles(); // Animation overlay selection let AnimationOverlay = null; if (animated && animationType !== 'none') { console.log('Card: Rendering animation type:', animationType, 'loop:', loop); if (animationType === 'shine') AnimationOverlay = <ShineOverlay loop={loop}/>; else if (animationType === 'sine-shine') AnimationOverlay = <SineShineOverlay loop={loop}/>; else if (animationType === 'stars') AnimationOverlay = <StarsOverlay loop={loop}/>; else if (animationType === 'drops') AnimationOverlay = <DropsOverlay loop={loop}/>; } else { console.log('Card: No animation, animated:', animated, 'animationType:', animationType); } return (<View key={key} style={[ getCardStyle(elevation, backgroundColor || colors.background.paper), style ]}> {AnimationOverlay} {image && <Image source={image} style={getImageStyle()}/>} {title && <Text style={getTitleStyle(colors.text.primary)}>{title}</Text>} {subtitle && <Text style={getSubtitleStyle(colors.text.secondary)}>{subtitle}</Text>} {children} </View>); };