expo-popcore-old/components/Button.tsx

156 lines
3.2 KiB
TypeScript

import React from 'react';
import {
Pressable,
Text,
ActivityIndicator,
StyleSheet,
ViewStyle,
StyleProp,
} from 'react-native';
import { Colors, Spacing, BorderRadius, FontSize, Animation } from '@/constants/theme';
type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text';
type ButtonSize = 'small' | 'medium' | 'large';
interface ButtonProps {
title: string;
onPress: () => void;
variant?: ButtonVariant;
size?: ButtonSize;
disabled?: boolean;
loading?: boolean;
fullWidth?: boolean;
icon?: React.ReactNode;
style?: StyleProp<ViewStyle>;
}
const config = {
variants: {
primary: {
backgroundColor: Colors.brand.primary,
borderWidth: 0,
borderColor: 'transparent',
textColor: '#FFFFFF',
},
secondary: {
backgroundColor: Colors.brand.secondary,
borderWidth: 0,
borderColor: 'transparent',
textColor: '#FFFFFF',
},
outline: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: Colors.brand.primary,
textColor: Colors.brand.primary,
},
text: {
backgroundColor: 'transparent',
borderWidth: 0,
borderColor: 'transparent',
textColor: Colors.brand.primary,
},
},
sizes: {
small: {
height: 36,
paddingHorizontal: Spacing.md,
fontSize: FontSize.sm,
},
medium: {
height: 48,
paddingHorizontal: Spacing.xl,
fontSize: FontSize.md,
},
large: {
height: 56,
paddingHorizontal: Spacing.xxl,
fontSize: FontSize.lg,
},
},
};
export const Button: React.FC<ButtonProps> = ({
title,
onPress,
variant = 'primary',
size = 'medium',
disabled = false,
loading = false,
fullWidth = false,
icon,
style,
}) => {
const variantConfig = config.variants[variant];
const sizeConfig = config.sizes[size];
const isInteractive = !disabled && !loading;
const buttonStyle = [
styles.base,
{
height: sizeConfig.height,
paddingHorizontal: sizeConfig.paddingHorizontal,
},
{
backgroundColor: variantConfig.backgroundColor,
borderWidth: variantConfig.borderWidth,
borderColor: variantConfig.borderColor,
opacity: disabled ? 0.5 : 1,
},
fullWidth && styles.fullWidth,
style,
];
const textStyle = [
styles.text,
{
color: variantConfig.textColor,
fontSize: sizeConfig.fontSize,
},
];
const indicatorColor = variant === 'outline' || variant === 'text'
? '#007AFF'
: '#FFFFFF';
return (
<Pressable
onPress={onPress}
disabled={!isInteractive}
style={({ pressed }) => [
buttonStyle,
isInteractive && pressed && styles.pressed,
]}
>
<>{icon}</>
{loading ? (
<ActivityIndicator size="small" color={indicatorColor} />
) : (
<Text style={textStyle}>{title}</Text>
)}
</Pressable>
);
};
const styles = StyleSheet.create({
base: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderRadius: BorderRadius.full,
fontWeight: '600',
},
fullWidth: {
width: '100%',
},
pressed: {
transform: [{ scale: Animation.scale.pressed }],
},
text: {
fontWeight: '600',
textAlign: 'center',
},
});
export default Button;