import React, { useCallback, useRef } from 'react'; import { ActivityIndicator, Animated, Pressable, StyleSheet, Text, View } from 'react-native'; import { useTheme } from '../../theme'; interface ButtonProps { label: string; onPress: () => void; variant?: 'primary' | 'secondary' | 'danger'; disabled?: boolean; /** 加载状态:显示 spinner 并禁止重复点击。 */ loading?: boolean; } /** 按钮(主题化,并有触控动效)。primary=主题色,secondary=次级容器,danger=错误色。支持 loading 状态。 */ export function Button({ label, onPress, variant = 'primary', disabled, loading }: ButtonProps) { const { theme } = useTheme(); const scale = useRef(new Animated.Value(1)).current; const isDisabled = disabled || loading; const handlePressIn = useCallback(() => { if (isDisabled) return; Animated.spring(scale, { toValue: 0.96, useNativeDriver: true, tension: 180, friction: 7, }).start(); }, [scale, isDisabled]); const handlePressOut = useCallback(() => { Animated.spring(scale, { toValue: 1.0, useNativeDriver: true, tension: 180, friction: 7, }).start(); }, [scale]); const bg = variant === 'primary' ? theme.colors.accent : variant === 'danger' ? theme.colors.error : theme.colors.bgTertiary; const fg = variant === 'secondary' ? theme.colors.fgPrimary : theme.colors.fgInverse; return ( {loading ? ( {label} ) : ( {label} )} ); } const styles = StyleSheet.create({ button: { paddingVertical: 12, paddingHorizontal: 16, alignItems: 'center' }, text: { fontWeight: '700' }, loadingRow: { flexDirection: 'row', alignItems: 'center', gap: 8 }, });