164 lines
4.1 KiB
TypeScript
164 lines
4.1 KiB
TypeScript
import { View, StyleSheet, TouchableOpacity } from 'react-native';
|
|
import { ThemedText } from '@/components/themed-text';
|
|
import { useThemeColor } from '@/hooks/use-theme-color';
|
|
import { FormFieldSchema } from '@/lib/types/template-run';
|
|
import { useState } from 'react';
|
|
|
|
interface SelectInputFieldProps {
|
|
field: FormFieldSchema;
|
|
value: string | number;
|
|
onChange: (value: string | number) => void;
|
|
error?: string;
|
|
}
|
|
|
|
export function SelectInputField({ field, value, onChange, error }: SelectInputFieldProps) {
|
|
const textColor = useThemeColor({}, 'text');
|
|
const borderColor = useThemeColor({}, 'border');
|
|
const errorColor = useThemeColor({}, 'error');
|
|
const backgroundColor = useThemeColor({}, 'background');
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const selectedOption = field.options?.find(option => option.value === value);
|
|
|
|
const handleSelect = (option: any) => {
|
|
onChange(option.value);
|
|
setIsOpen(false);
|
|
};
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{field.label && (
|
|
<ThemedText style={styles.label}>
|
|
{field.label}
|
|
{field.required && <ThemedText style={[styles.required, { color: errorColor }]}> *</ThemedText>}
|
|
</ThemedText>
|
|
)}
|
|
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.selectBox,
|
|
{
|
|
borderColor: error ? errorColor : borderColor,
|
|
backgroundColor,
|
|
}
|
|
]}
|
|
onPress={() => setIsOpen(!isOpen)}
|
|
activeOpacity={0.8}
|
|
>
|
|
<ThemedText style={[styles.selectedText, { color: selectedOption ? textColor : '#999' }]}>
|
|
{selectedOption?.label || field.placeholder || '请选择...'}
|
|
</ThemedText>
|
|
<ThemedText style={[styles.arrow, { color: textColor }]}>
|
|
{isOpen ? '▲' : '▼'}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
|
|
{isOpen && field.options && (
|
|
<View style={[styles.dropdown, { backgroundColor, borderColor }]}>
|
|
{field.options.map((option, index) => (
|
|
<TouchableOpacity
|
|
key={index}
|
|
style={[
|
|
styles.option,
|
|
{
|
|
backgroundColor: option.value === value ? borderColor : 'transparent',
|
|
borderBottomColor: borderColor,
|
|
}
|
|
]}
|
|
onPress={() => handleSelect(option)}
|
|
activeOpacity={0.7}
|
|
>
|
|
<ThemedText style={[
|
|
styles.optionText,
|
|
{
|
|
color: option.value === value ? '#fff' : textColor,
|
|
fontWeight: option.value === value ? '600' : '400',
|
|
}
|
|
]}>
|
|
{option.label}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
)}
|
|
|
|
{field.description && (
|
|
<ThemedText style={styles.description}>{field.description}</ThemedText>
|
|
)}
|
|
|
|
{error && (
|
|
<ThemedText style={[styles.errorText, { color: errorColor }]}>
|
|
{error}
|
|
</ThemedText>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
marginBottom: 16,
|
|
position: 'relative',
|
|
zIndex: 10,
|
|
},
|
|
label: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
marginBottom: 8,
|
|
},
|
|
required: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
selectBox: {
|
|
borderWidth: 1,
|
|
borderRadius: 8,
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 10,
|
|
minHeight: 44,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
},
|
|
selectedText: {
|
|
fontSize: 16,
|
|
flex: 1,
|
|
},
|
|
arrow: {
|
|
fontSize: 12,
|
|
marginLeft: 8,
|
|
},
|
|
dropdown: {
|
|
borderWidth: 1,
|
|
borderRadius: 8,
|
|
marginTop: 4,
|
|
maxHeight: 200,
|
|
elevation: 3,
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 4,
|
|
position: 'absolute',
|
|
top: '100%',
|
|
left: 0,
|
|
right: 0,
|
|
zIndex: 20,
|
|
},
|
|
option: {
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 10,
|
|
borderBottomWidth: 1,
|
|
},
|
|
optionText: {
|
|
fontSize: 16,
|
|
},
|
|
description: {
|
|
fontSize: 12,
|
|
opacity: 0.7,
|
|
marginTop: 4,
|
|
},
|
|
errorText: {
|
|
fontSize: 12,
|
|
marginTop: 4,
|
|
},
|
|
}); |