Files
mindfulness/client/components/home/ThemeModal.tsx
2026-02-05 16:06:49 +08:00

158 lines
3.8 KiB
TypeScript

import React from 'react';
import { Image, Pressable, StyleSheet, Text, View } from 'react-native';
import { useTranslation } from 'react-i18next';
import SheetModal from '@/components/ui/SheetModal';
import type { ThemeMode } from '@/src/storage/appStorage';
type Props = {
visible: boolean;
mode: ThemeMode;
onSelect: (mode: ThemeMode) => void;
onClose: () => void;
};
export default function ThemeModal({ visible, mode, onSelect, onClose }: Props) {
const { t } = useTranslation();
return (
<SheetModal visible={visible} title={t('theme.title')} onClose={onClose} height={360}>
<View style={styles.row}>
<ThemeCard
title={t('theme.scenery')}
selected={mode === 'scenery'}
onPress={() => onSelect('scenery')}
>
<Image
source={require('../../assets/images/theme/theme_landscape.png')}
resizeMode="cover"
style={styles.previewImage}
/>
</ThemeCard>
<ThemeCard
title={t('theme.color')}
selected={mode === 'color'}
onPress={() => onSelect('color')}
>
<Image
source={require('../../assets/images/theme/theme_color.png')}
resizeMode="cover"
style={styles.previewImage}
/>
</ThemeCard>
<ThemeCard
title={t('theme.suixin')}
selected={mode === 'suixin'}
onPress={() => onSelect('suixin')}
>
<Image
// 占位:一期复用纯色预览图,后续可替换为专用资源
source={require('../../assets/images/theme/theme_color.png')}
resizeMode="cover"
style={styles.previewImage}
/>
</ThemeCard>
</View>
</SheetModal>
);
}
function ThemeCard({
title,
selected,
onPress,
children,
}: {
title: string;
selected: boolean;
onPress: () => void;
children: React.ReactNode;
}) {
return (
<Pressable
onPress={onPress}
style={styles.cardContainer}
hitSlop={6}
>
<View style={[styles.previewWrapper, selected && styles.selectedWrapper]}>
<View style={styles.previewInner}>
{children}
{/* 文案展示在图片中心 */}
<View style={styles.textOverlay}>
<Text
style={[styles.overlayTitle, selected && styles.selectedOverlayTitle]}
numberOfLines={1}
adjustsFontSizeToFit
minimumFontScale={0.85}
>
{title}
</Text>
</View>
</View>
</View>
</Pressable>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
flexWrap: 'nowrap',
gap: 12,
paddingHorizontal: 4,
paddingBottom: 50,
paddingTop: 20,
justifyContent: 'space-between',
},
cardContainer: {
flex: 1,
minWidth: 0,
alignItems: 'stretch',
},
previewWrapper: {
width: '100%',
aspectRatio: 110 / 178,
borderRadius: 26,
padding: 6.5,
justifyContent: 'center',
alignItems: 'center',
},
selectedWrapper: {
borderWidth: 4,
borderColor: '#E7837A',
borderRadius: 26,
},
previewInner: {
width: '100%',
height: '100%',
borderRadius: 21,
overflow: 'hidden',
backgroundColor: '#F5F5F5',
position: 'relative',
},
previewImage: {
width: '100%',
height: '100%',
},
textOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.05)', // 轻微遮罩增加文字可读性
},
overlayTitle: {
color: '#FFFFFF',
fontSize: 15,
fontWeight: '700',
textAlign: 'center',
textShadowColor: 'rgba(0, 0, 0, 0.3)',
textShadowOffset: { width: 0, height: 1 },
textShadowRadius: 3,
},
selectedOverlayTitle: {
color: '#FFFFFF',
},
});