Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { | |
| ActivityIndicator, | |
| Pressable, | |
| StyleProp, | |
| StyleSheet, | |
| Text, | |
| View, | |
| ViewStyle, | |
| } from 'react-native'; | |
| import { colors, radius, spacing, typography } from '../theme'; | |
| interface Props { | |
| label: string; | |
| onPress: () => void; | |
| variant?: 'primary' | 'secondary' | 'danger' | 'ghost'; | |
| disabled?: boolean; | |
| loading?: boolean; | |
| icon?: string; // emoji | |
| style?: StyleProp<ViewStyle>; | |
| } | |
| export default function UploadButton({ | |
| label, | |
| onPress, | |
| variant = 'primary', | |
| disabled, | |
| loading, | |
| icon, | |
| style, | |
| }: Props) { | |
| const isDisabled = disabled || loading; | |
| return ( | |
| <Pressable | |
| accessibilityRole="button" | |
| accessibilityState={{ disabled: isDisabled }} | |
| onPress={onPress} | |
| disabled={isDisabled} | |
| style={({ pressed }) => [ | |
| styles.base, | |
| styles[variant], | |
| pressed && !isDisabled && styles.pressed, | |
| isDisabled && styles.disabled, | |
| style, | |
| ]} | |
| > | |
| <View style={styles.content}> | |
| {loading ? ( | |
| <ActivityIndicator color={variant === 'secondary' ? colors.text : '#fff'} /> | |
| ) : ( | |
| <> | |
| {icon ? <Text style={styles.icon}>{icon}</Text> : null} | |
| <Text style={[styles.label, variant === 'secondary' && styles.labelSecondary]}> | |
| {label} | |
| </Text> | |
| </> | |
| )} | |
| </View> | |
| </Pressable> | |
| ); | |
| } | |
| const styles = StyleSheet.create({ | |
| base: { | |
| minHeight: 48, | |
| paddingHorizontal: spacing.xl, | |
| paddingVertical: spacing.md, | |
| borderRadius: radius.md, | |
| justifyContent: 'center', | |
| alignItems: 'center', | |
| }, | |
| content: { | |
| flexDirection: 'row', | |
| alignItems: 'center', | |
| gap: spacing.sm, | |
| }, | |
| primary: { backgroundColor: colors.primary }, | |
| secondary: { | |
| backgroundColor: 'transparent', | |
| borderWidth: 1, | |
| borderColor: colors.borderMuted, | |
| }, | |
| danger: { backgroundColor: colors.danger }, | |
| ghost: { backgroundColor: 'transparent' }, | |
| pressed: { opacity: 0.85 }, | |
| disabled: { opacity: 0.5 }, | |
| icon: { fontSize: 18, marginRight: spacing.xs, color: '#fff' }, | |
| label: { | |
| ...typography.bodyBold, | |
| color: '#fff', | |
| }, | |
| labelSecondary: { | |
| color: colors.text, | |
| }, | |
| }); | |