A mobile app view of atmoquest
0

Configure Feed

Select the types of activity you want to include in your feed.

atmoquest-mobile / components / Button.tsx
1.9 kB 73 lines
1import { TouchableOpacity, Text, StyleSheet, type ViewStyle } from 'react-native'; 2import * as Haptics from 'expo-haptics'; 3import { colors, fontFamilies, radii } from '@/theme'; 4 5type ButtonVariant = 'primary' | 'ghost' | 'danger'; 6 7interface ButtonProps { 8 label: string; 9 variant?: ButtonVariant; 10 onPress: () => void; 11 disabled?: boolean; 12 style?: ViewStyle; 13} 14 15const variantMap: Record<ButtonVariant, { bg: string; text: string; border: string }> = { 16 primary: { bg: colors.peach, text: colors.crust, border: colors.peach }, 17 ghost: { bg: 'transparent', text: colors.lavender, border: colors.surface1 }, 18 danger: { bg: 'transparent', text: colors.red, border: colors.red }, 19}; 20 21/** 22 * Button matching the web `.btn-primary`, `.btn-ghost` styles. 23 * Includes light haptic on press. 24 */ 25export function Button({ label, variant = 'primary', onPress, disabled, style }: ButtonProps) { 26 const v = variantMap[variant]; 27 28 const handlePress = () => { 29 Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); 30 onPress(); 31 }; 32 33 return ( 34 <TouchableOpacity 35 onPress={handlePress} 36 disabled={disabled} 37 activeOpacity={0.7} 38 style={[ 39 styles.btn, 40 { 41 backgroundColor: disabled ? colors.surface0 : v.bg, 42 borderColor: disabled ? colors.surface0 : v.border, 43 }, 44 style, 45 ]} 46 > 47 <Text 48 style={[ 49 styles.label, 50 { color: disabled ? colors.muted : v.text }, 51 ]} 52 > 53 {label} 54 </Text> 55 </TouchableOpacity> 56 ); 57} 58 59const styles = StyleSheet.create({ 60 btn: { 61 paddingHorizontal: 20, 62 paddingVertical: 12, 63 borderRadius: radii.md, 64 borderWidth: 1, 65 alignItems: 'center', 66 justifyContent: 'center', 67 }, 68 label: { 69 fontFamily: fontFamilies.mono, 70 fontSize: 14, 71 fontWeight: '600', 72 }, 73});