expo-popcore-old/components/sker/generate-btn/index.tsx

222 lines
5.0 KiB
TypeScript

import { Image as ExpoImage } from 'expo-image';
import { useEffect, useState } from 'react';
import {
Animated,
Easing,
Pressable,
StyleSheet,
Text,
TouchableWithoutFeedback,
View,
} from 'react-native';
const LoadingIcon: React.FC = () => {
const [spinValue] = useState(new Animated.Value(0));
useEffect(() => {
const animation = Animated.loop(
Animated.timing(spinValue, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: false,
})
);
animation.start();
return () => {
animation.stop();
};
}, []);
const spin = spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
return (
<Animated.Image
source={require('@/assets/images/loading.png')}
style={[styles.icon, { transform: [{ rotate: spin }] }]}
/>
);
};
export type MenuItem = {
icon: React.ReactNode;
label: string;
onPress: () => void;
};
export type GenerateBtnProps = {
onGenerate: () => void;
menuItems?: MenuItem[];
loading?: boolean;
};
export const GenerateBtn: React.FC<React.PropsWithChildren & GenerateBtnProps> = ({ onGenerate, menuItems = [], loading = false, children }) => {
const [menuVisible, setMenuVisible] = useState(false);
const handleGenerate = () => {
onGenerate();
};
const handleMenuItemPress = (onPress: () => void) => {
setMenuVisible(false);
onPress();
};
return (
<View style={styles.wrapper}>
<View style={{ backgroundColor: `#171717`, borderRadius: 8 }}>
{children}
<View style={styles.container}>
<Pressable
style={({ pressed }) => [
styles.generateButton,
pressed && styles.pressed,
]}
onPress={handleGenerate}
disabled={loading}
>
{loading ? (
<LoadingIcon />
) : (
<ExpoImage source={require('@/assets/images/start.png')} style={styles.icon} />
)}
<Text style={styles.buttonText}>Generate Video</Text>
</Pressable>
{menuItems && menuItems.length > 0 && <Pressable
style={({ pressed }) => [
styles.moreButton,
pressed && styles.pressed,
]}
onPress={() => setMenuVisible(true)}
>
<ExpoImage source={require('@/assets/images/more.png')} style={styles.icon} />
</Pressable>}
</View>
</View>
{menuVisible && (
<>
<TouchableWithoutFeedback onPress={() => setMenuVisible(false)}>
<View style={styles.backdrop} />
</TouchableWithoutFeedback>
<View style={styles.menuWrapper}>
<View style={styles.menu}>
{menuItems.map((item, index) => (
<Pressable
key={index}
style={({ pressed }) => [
styles.menuItem,
pressed && styles.menuItemPressed,
]}
onPress={() => handleMenuItemPress(item.onPress)}
>
<View style={styles.menuIcon}>{item.icon}</View>
<Text style={styles.menuLabel}>{item.label}</Text>
</Pressable>
))}
</View>
</View>
</>
)}
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
position: 'relative',
},
container: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
marginHorizontal: 12,
marginVertical: 27,
backgroundColor: '#171717'
},
generateButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
backgroundColor: '#D1FE17',
borderRadius: 12,
paddingVertical: 16,
paddingHorizontal: 24,
},
moreButton: {
width: 56,
height: 56,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#2E3031',
borderRadius: 12,
},
pressed: {
opacity: 0.7,
},
icon: {
width: 24,
height: 24,
},
buttonText: {
fontSize: 16,
fontWeight: '600',
color: '#000000',
letterSpacing: 0.2,
},
backdrop: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 999,
},
menuWrapper: {
position: 'absolute',
top: 90,
right: 12,
zIndex: 1000,
},
menu: {
width: 140,
backgroundColor: '#1F1F1F',
borderRadius: 8,
paddingVertical: 4,
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 12,
gap: 12,
},
menuItemPressed: {
backgroundColor: 'rgba(255, 255, 255, 0.1)',
},
menuIcon: {
width: 24,
height: 24,
alignItems: 'center',
justifyContent: 'center',
},
menuLabel: {
flex: 1,
fontSize: 14,
fontWeight: '500',
color: '#FFFFFF',
},
});