import React, { useEffect, useMemo, useRef, useState } from 'react'; import { View, StyleSheet, TextInput, Platform, Animated, TouchableOpacity, Text, Keyboard, Pressable, useWindowDimensions } from 'react-native'; import { useTranslation } from 'react-i18next'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { OnboardingColors } from '@/constants/OnboardingTheme'; import BtnNotClicked from '@/assets/images/icon/btn_Notclicked.svg'; import BtnClicked from '@/assets/images/icon/btn_clicked.svg'; import EnterLightIcon from '@/assets/images/icon/enter_Light_icon.svg'; interface NameInputStepProps { value: string; onChangeText: (text: string) => void; onNext: () => void; } export function NameInputStep({ value, onChangeText, onNext }: NameInputStepProps) { const { t } = useTranslation(); const insets = useSafeAreaInsets(); const { width, height } = useWindowDimensions(); const isTablet = Platform.OS === 'ios' && Math.min(width, height) >= 768; const [isFocused, setIsFocused] = useState(false); const [keyboardHeight, setKeyboardHeight] = useState(0); const blinkAnim = useRef(new Animated.Value(1)).current; const hasInput = value.trim().length > 0; const inputCardWidth = isTablet ? Math.min(520, Math.floor(width * 0.72)) : 335; useEffect(() => { const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'; const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'; const subShow = Keyboard.addListener(showEvent, (e) => { setKeyboardHeight(e.endCoordinates?.height ?? 0); }); const subHide = Keyboard.addListener(hideEvent, () => { setKeyboardHeight(0); }); return () => { subShow.remove(); subHide.remove(); }; }, []); useEffect(() => { const animation = Animated.loop( Animated.sequence([ Animated.timing(blinkAnim, { toValue: 0, duration: 500, useNativeDriver: true }), Animated.timing(blinkAnim, { toValue: 1, duration: 500, useNativeDriver: true }), ]) ); if (isFocused) { animation.start(); } else { animation.stop(); blinkAnim.setValue(0); } return () => animation.stop(); }, [blinkAnim, isFocused]); const footerBottom = useMemo(() => { // iOS 的 keyboard height 通常已包含底部安全区,避免重复叠加 const keyboardOffset = Math.max(0, keyboardHeight - insets.bottom); return 16 + insets.bottom + keyboardOffset; }, [insets.bottom, keyboardHeight]); return ( {/* 显示层:文案 + 跟随的光标 */} {hasInput ? value : isFocused ? '' : t('onboardingSurvey.steps.name.placeholder')} {isFocused && ( )} {/* 交互层:隐藏的输入框 */} setIsFocused(true)} onBlur={() => setIsFocused(false)} caretHidden={true} autoCorrect={false} spellCheck={false} returnKeyType="done" blurOnSubmit={true} onSubmitEditing={() => { Keyboard.dismiss(); // 不再自動跳頁,僅收起鍵盤;前進需點擊底部 ➡️ }} /> { Keyboard.dismiss(); onNext(); }} disabled={!hasInput} activeOpacity={0.8} > {hasInput ? : } ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', paddingTop: 20, }, inputCard: { width: 335, height: 75, backgroundColor: OnboardingColors.cardBackground, borderRadius: 20, justifyContent: 'center', alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 10, elevation: 2, }, inputWrapper: { width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center', }, displayLayer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, displayText: { fontSize: 22, fontFamily: Platform.select({ ios: 'STIX Two Text', android: 'serif', default: 'serif' }), fontWeight: '600', color: OnboardingColors.textPrimary, textAlign: 'center', }, hiddenInput: { ...StyleSheet.absoluteFillObject, color: 'transparent', // 文字透明,只负责输入逻辑 fontSize: 22, textAlign: 'center', }, cursorWrapper: { // 默认居中显示时,光标在左侧 }, footer: { position: 'absolute', alignItems: 'center', } });