Files
mindfulness/client/components/onboarding/SelectionStep.tsx
2026-03-03 19:55:53 +08:00

132 lines
3.7 KiB
TypeScript

import React from 'react';
import { View, StyleSheet, TouchableOpacity, ScrollView, Text, Platform, useWindowDimensions } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { OnboardingColors, OnboardingFont } from '@/constants/OnboardingTheme';
import BtnNotClicked from '@/assets/images/icon/btn_Notclicked.svg';
import BtnClicked from '@/assets/images/icon/btn_clicked.svg';
interface Option {
id: string;
label: string;
}
interface SelectionStepProps {
options: Option[];
selectedIds: string[];
onToggle: (id: string) => void;
onNext: () => void;
onSkip?: () => void;
}
export function SelectionStep({ options, selectedIds, onToggle, onNext, onSkip }: SelectionStepProps) {
const { width, height } = useWindowDimensions();
const isTablet = Platform.OS === 'ios' && Math.min(width, height) >= 768;
const maxOptionWidth = isTablet ? 560 : undefined;
const hasSelection = selectedIds.length > 0;
const insets = useSafeAreaInsets();
const footerBottom = insets.bottom + (isTablet ? 38 : 28);
const footerButtonHeight = 57;
// 底部留白加大,避免最后一项与按钮边框视觉重叠
const footerPaddingBottom = footerBottom + footerButtonHeight + 40;
return (
<View style={styles.container}>
<ScrollView
style={styles.scroll}
showsVerticalScrollIndicator={false}
contentContainerStyle={[
styles.optionsList,
{
paddingBottom: footerPaddingBottom,
alignItems: 'center',
},
]}
>
{options.map((option) => {
const isSelected = selectedIds.includes(option.id);
return (
<TouchableOpacity
key={option.id}
style={[
styles.optionCard,
maxOptionWidth ? { maxWidth: maxOptionWidth } : null,
isSelected && styles.optionCardSelected,
]}
onPress={() => onToggle(option.id)}
activeOpacity={0.7}
>
<Text style={styles.optionText}>{option.label}</Text>
</TouchableOpacity>
);
})}
</ScrollView>
{/* 底部按钮:距离底部 12% 高度 */}
<View style={[styles.footer, { bottom: footerBottom }]}>
<TouchableOpacity onPress={onNext} disabled={!hasSelection} activeOpacity={0.8}>
{hasSelection ? <BtnClicked width={87} height={57} /> : <BtnNotClicked width={87} height={57} />}
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 8,
},
scroll: {
flex: 1,
},
optionsList: {
// paddingBottom 由安全区 + 按钮高度动态计算,避免选项被遮住
},
optionCard: {
width: '100%',
height: 75,
backgroundColor: OnboardingColors.cardBackground,
borderRadius: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 24,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 10,
elevation: 2,
},
optionCardSelected: {
backgroundColor: OnboardingColors.cardSelected,
},
optionText: {
fontSize: 18,
color: OnboardingColors.textPrimary,
fontWeight: '500',
fontFamily: OnboardingFont.question,
},
footer: {
position: 'absolute',
left: 0,
right: 0,
alignItems: 'center',
},
footerRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 16,
},
skipBtn: {
paddingVertical: 10,
paddingHorizontal: 14,
borderRadius: 12,
backgroundColor: 'rgba(0,0,0,0.04)',
},
skipText: {
fontSize: 16,
color: OnboardingColors.textMuted,
},
});