IOS小组件/文案
This commit is contained in:
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import SheetModal from '@/components/ui/SheetModal';
|
||||
|
||||
export type ThemeMode = 'scenery' | 'color';
|
||||
import type { ThemeMode } from '@/src/storage/appStorage';
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
@@ -16,7 +16,7 @@ type Props = {
|
||||
export default function ThemeModal({ visible, mode, onSelect, onClose }: Props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SheetModal visible={visible} title={t('theme.title')} onClose={onClose} height={350}>
|
||||
<SheetModal visible={visible} title={t('theme.title')} onClose={onClose} height={360}>
|
||||
<View style={styles.row}>
|
||||
<ThemeCard
|
||||
title={t('theme.scenery')}
|
||||
@@ -41,6 +41,19 @@ export default function ThemeModal({ visible, mode, onSelect, onClose }: Props)
|
||||
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>
|
||||
);
|
||||
@@ -81,19 +94,20 @@ function ThemeCard({
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
gap: 30,
|
||||
paddingHorizontal: 10,
|
||||
flexWrap: 'wrap',
|
||||
gap: 12,
|
||||
paddingHorizontal: 0,
|
||||
paddingBottom: 50,
|
||||
paddingTop: 20,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
cardContainer: {
|
||||
alignItems: 'center',
|
||||
width: 143,
|
||||
width: 112,
|
||||
},
|
||||
previewWrapper: {
|
||||
width: 138,
|
||||
height: 203,
|
||||
width: 110,
|
||||
height: 178,
|
||||
borderRadius: 26,
|
||||
padding: 6.5,
|
||||
justifyContent: 'center',
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { View, StyleSheet, TextInput, Platform, Animated, TouchableOpacity, Dimensions, Text } from 'react-native';
|
||||
import { View, StyleSheet, TextInput, Platform, Animated, TouchableOpacity, Text } 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';
|
||||
|
||||
const { height } = Dimensions.get('window');
|
||||
|
||||
interface NameInputStepProps {
|
||||
value: string;
|
||||
onChangeText: (text: string) => void;
|
||||
@@ -16,6 +15,7 @@ interface NameInputStepProps {
|
||||
|
||||
export function NameInputStep({ value, onChangeText, onNext }: NameInputStepProps) {
|
||||
const { t } = useTranslation();
|
||||
const insets = useSafeAreaInsets();
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const blinkAnim = useRef(new Animated.Value(1)).current;
|
||||
const hasInput = value.trim().length > 0;
|
||||
@@ -71,7 +71,7 @@ export function NameInputStep({ value, onChangeText, onNext }: NameInputStepProp
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<View style={[styles.footer, { bottom: insets.bottom + 16 }]}>
|
||||
<TouchableOpacity
|
||||
onPress={onNext}
|
||||
disabled={!hasInput}
|
||||
@@ -132,7 +132,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: height * 0.12,
|
||||
alignItems: 'center',
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, TouchableOpacity, Text, Platform, Dimensions } from 'react-native';
|
||||
import { View, StyleSheet, TouchableOpacity, Text, Platform } from 'react-native';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { OnboardingColors } from '@/constants/OnboardingTheme';
|
||||
import AddIcon from '@/assets/images/icon/add_icon.svg';
|
||||
import ReduceIcon from '@/assets/images/icon/reduce_icon.svg';
|
||||
import BtnClicked from '@/assets/images/icon/btn_clicked.svg';
|
||||
|
||||
const { height } = Dimensions.get('window');
|
||||
|
||||
interface ReminderStepProps {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
@@ -17,6 +16,7 @@ interface ReminderStepProps {
|
||||
|
||||
export function ReminderStep({ value, onChange, onFinish, onSkip }: ReminderStepProps) {
|
||||
const { t } = useTranslation();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const handleReduce = () => {
|
||||
// 允许 0~5;0 表示关闭每日提醒
|
||||
@@ -44,7 +44,7 @@ export function ReminderStep({ value, onChange, onFinish, onSkip }: ReminderStep
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<View style={[styles.footer, { bottom: insets.bottom + 16 }]}>
|
||||
<TouchableOpacity onPress={onFinish} activeOpacity={0.8}>
|
||||
<BtnClicked width={87} height={57} />
|
||||
</TouchableOpacity>
|
||||
@@ -92,7 +92,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: height * 0.12,
|
||||
alignItems: 'center',
|
||||
}
|
||||
,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, TouchableOpacity, ScrollView, Dimensions } from 'react-native';
|
||||
import { View, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { OnboardingColors } from '@/constants/OnboardingTheme';
|
||||
import { SerifText } from './SerifText';
|
||||
import SelectedIcon from '@/assets/images/icon/selected_icon.svg';
|
||||
import BtnNotClicked from '@/assets/images/icon/btn_Notclicked.svg';
|
||||
import BtnClicked from '@/assets/images/icon/btn_clicked.svg';
|
||||
|
||||
const { height } = Dimensions.get('window');
|
||||
|
||||
interface Option {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -23,10 +22,18 @@ interface SelectionStepProps {
|
||||
|
||||
export function SelectionStep({ options, selectedIds, onToggle, onNext, onSkip }: SelectionStepProps) {
|
||||
const hasSelection = selectedIds.length > 0;
|
||||
const insets = useSafeAreaInsets();
|
||||
const footerBottom = insets.bottom + 16;
|
||||
const footerButtonHeight = 57;
|
||||
const footerPaddingBottom = footerBottom + footerButtonHeight + 24;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.optionsList}>
|
||||
<ScrollView
|
||||
style={styles.scroll}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={[styles.optionsList, { paddingBottom: footerPaddingBottom }]}
|
||||
>
|
||||
{options.map((option) => {
|
||||
const isSelected = selectedIds.includes(option.id);
|
||||
return (
|
||||
@@ -48,7 +55,7 @@ export function SelectionStep({ options, selectedIds, onToggle, onNext, onSkip }
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部按钮:距离底部 12% 高度 */}
|
||||
<View style={styles.footer}>
|
||||
<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>
|
||||
@@ -62,8 +69,11 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
paddingTop: 20,
|
||||
},
|
||||
scroll: {
|
||||
flex: 1,
|
||||
},
|
||||
optionsList: {
|
||||
paddingBottom: 150, // 为底部按钮留出空间
|
||||
// paddingBottom 由安全区 + 按钮高度动态计算,避免选项被遮住
|
||||
},
|
||||
optionCard: {
|
||||
width: '100%',
|
||||
@@ -92,7 +102,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: height * 0.12,
|
||||
left: 0,
|
||||
right: 0,
|
||||
alignItems: 'center',
|
||||
|
||||
Reference in New Issue
Block a user