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