A mobile app view of atmoquest
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});