mirror of
http://88.130.71.182:3000/BlitTech/contexta_mb.git
synced 2026-06-12 23:23:22 +00:00
Initial commit
This commit is contained in:
132
src/components/ui/Button.tsx
Normal file
132
src/components/ui/Button.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import React, { useRef } from 'react';
|
||||
import {
|
||||
Animated,
|
||||
TouchableWithoutFeedback,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ActivityIndicator,
|
||||
ViewStyle,
|
||||
TextStyle,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { COLORS, RADIUS, SPACING, FONT_SIZE, FONT, SHADOWS } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
type Variant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
||||
type Size = 'sm' | 'md' | 'lg';
|
||||
|
||||
interface ButtonProps {
|
||||
title: string;
|
||||
onPress: () => void;
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
style?: ViewStyle;
|
||||
textStyle?: TextStyle;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
export function Button({
|
||||
title,
|
||||
onPress,
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
loading = false,
|
||||
disabled = false,
|
||||
style,
|
||||
textStyle,
|
||||
fullWidth = false,
|
||||
}: ButtonProps) {
|
||||
const { theme, isDark } = useTheme();
|
||||
const scale = useRef(new Animated.Value(1)).current;
|
||||
|
||||
const onPressIn = () =>
|
||||
Animated.spring(scale, { toValue: 0.96, useNativeDriver: true, speed: 50, bounciness: 0 }).start();
|
||||
|
||||
const onPressOut = () =>
|
||||
Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 30, bounciness: 4 }).start();
|
||||
|
||||
const containerBg = getContainerBg(variant, theme, isDark);
|
||||
const labelColor = getLabelColor(variant);
|
||||
const shadow = variant === 'primary' ? SHADOWS.primary : variant === 'danger' ? SHADOWS.sm : {};
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback
|
||||
onPress={onPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
disabled={disabled || loading}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.base,
|
||||
styles[`size_${size}`],
|
||||
containerBg,
|
||||
shadow,
|
||||
fullWidth && styles.fullWidth,
|
||||
(disabled || loading) && styles.disabled,
|
||||
{ transform: [{ scale }] },
|
||||
style,
|
||||
]}>
|
||||
{loading ? (
|
||||
<ActivityIndicator
|
||||
size="small"
|
||||
color={variant === 'primary' || variant === 'danger' ? COLORS.white : COLORS.primary}
|
||||
/>
|
||||
) : (
|
||||
<Text style={[styles.label, styles[`label_${size}`], { color: labelColor }, textStyle]}>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
</Animated.View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
function getContainerBg(variant: Variant, theme: any, isDark: boolean): ViewStyle {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
return { backgroundColor: COLORS.primary };
|
||||
case 'secondary':
|
||||
return { backgroundColor: isDark ? theme.surfaceHover : theme.bgSecondary };
|
||||
case 'outline':
|
||||
return { backgroundColor: 'transparent', borderWidth: 1.5, borderColor: COLORS.primary };
|
||||
case 'ghost':
|
||||
return { backgroundColor: 'transparent' };
|
||||
case 'danger':
|
||||
return { backgroundColor: COLORS.error };
|
||||
}
|
||||
}
|
||||
|
||||
function getLabelColor(variant: Variant): string {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
case 'danger':
|
||||
return COLORS.white;
|
||||
case 'secondary':
|
||||
return COLORS.primary;
|
||||
case 'outline':
|
||||
case 'ghost':
|
||||
return COLORS.primary;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
borderRadius: RADIUS.md,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
fullWidth: { width: '100%' },
|
||||
disabled: { opacity: 0.55 },
|
||||
|
||||
size_sm: { paddingVertical: SPACING.xs + 2, paddingHorizontal: SPACING.md, borderRadius: RADIUS.sm },
|
||||
size_md: { paddingVertical: SPACING.md - 1, paddingHorizontal: SPACING.xl, borderRadius: RADIUS.md },
|
||||
size_lg: { paddingVertical: SPACING.md + 1, paddingHorizontal: SPACING.xxl, borderRadius: RADIUS.lg, minHeight: 52 },
|
||||
|
||||
label: { fontWeight: FONT.semibold as any, letterSpacing: 0.1 },
|
||||
label_sm: { fontSize: FONT_SIZE.sm },
|
||||
label_md: { fontSize: FONT_SIZE.md },
|
||||
label_lg: { fontSize: FONT_SIZE.md },
|
||||
});
|
||||
Reference in New Issue
Block a user