163 lines
4.0 KiB
JavaScript
163 lines
4.0 KiB
JavaScript
import React, { useState } from 'react';
|
||
import {
|
||
View,
|
||
Text,
|
||
StyleSheet,
|
||
TouchableOpacity,
|
||
Modal,
|
||
FlatList,
|
||
TouchableWithoutFeedback,
|
||
} from 'react-native';
|
||
import { Ionicons } from '@expo/vector-icons';
|
||
import { COLORS } from '../constants/colors';
|
||
|
||
/**
|
||
* SelectInput – simple dropdown selector that mimics the look of Input component.
|
||
*
|
||
* Props:
|
||
* - label : string – Field label
|
||
* - value : any – Currently selected value
|
||
* - options : Array<{ label: string, value: any }>
|
||
* - onValueChange : (val) => void
|
||
* - placeholder : string – Text when no value selected
|
||
* - disabled : boolean
|
||
*/
|
||
const SelectInput = ({
|
||
label,
|
||
value,
|
||
options = [],
|
||
onValueChange,
|
||
placeholder = 'Select',
|
||
disabled = false,
|
||
error = false,
|
||
}) => {
|
||
const [modalVisible, setModalVisible] = useState(false);
|
||
|
||
const selectedLabel = () => {
|
||
const found = options.find((o) => o.value === value);
|
||
return found ? found.label : placeholder;
|
||
};
|
||
|
||
const openModal = () => {
|
||
if (disabled) return;
|
||
setModalVisible(true);
|
||
};
|
||
|
||
const closeModal = () => setModalVisible(false);
|
||
|
||
const handleSelect = (val) => {
|
||
onValueChange && onValueChange(val);
|
||
closeModal();
|
||
};
|
||
|
||
return (
|
||
<View style={styles.container}>
|
||
{label && (
|
||
<Text style={[styles.label, typeof label === 'string' && label.trim().startsWith('*') && styles.requiredLabel]}>{label}</Text>
|
||
)}
|
||
<TouchableOpacity
|
||
style={[styles.selectBox, disabled && styles.disabled, error && styles.error]}
|
||
onPress={openModal}
|
||
activeOpacity={0.7}
|
||
>
|
||
<Text
|
||
style={[styles.selectText, !value && { color: COLORS.gray[400] }]}
|
||
numberOfLines={1}
|
||
>
|
||
{selectedLabel()}
|
||
</Text>
|
||
<Ionicons name="chevron-down" size={18} color={COLORS.gray[400]} />
|
||
</TouchableOpacity>
|
||
|
||
<Modal
|
||
animationType="slide"
|
||
transparent
|
||
visible={modalVisible}
|
||
onRequestClose={closeModal}
|
||
>
|
||
<TouchableWithoutFeedback onPress={closeModal}>
|
||
<View style={styles.modalOverlay}>
|
||
<TouchableWithoutFeedback>
|
||
<View style={styles.modalContainer}>
|
||
<FlatList
|
||
data={options}
|
||
keyExtractor={(item) => String(item.value)}
|
||
renderItem={({ item }) => (
|
||
<TouchableOpacity
|
||
style={styles.optionRow}
|
||
onPress={() => handleSelect(item.value)}
|
||
>
|
||
<Text style={styles.optionText}>{item.label}</Text>
|
||
</TouchableOpacity>
|
||
)}
|
||
/>
|
||
</View>
|
||
</TouchableWithoutFeedback>
|
||
</View>
|
||
</TouchableWithoutFeedback>
|
||
</Modal>
|
||
</View>
|
||
);
|
||
};
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
marginBottom: 20,
|
||
},
|
||
label: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
color: COLORS.textPrimary,
|
||
marginBottom: 8,
|
||
},
|
||
requiredLabel: {
|
||
color: COLORS.error,
|
||
},
|
||
selectBox: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
borderWidth: 1,
|
||
borderColor: COLORS.gray[300],
|
||
borderRadius: 12,
|
||
backgroundColor: COLORS.white,
|
||
paddingHorizontal: 16,
|
||
minHeight: 52,
|
||
},
|
||
selectText: {
|
||
fontSize: 16,
|
||
color: COLORS.textPrimary,
|
||
flex: 1,
|
||
marginRight: 8,
|
||
},
|
||
disabled: {
|
||
backgroundColor: COLORS.gray[100],
|
||
opacity: 0.6,
|
||
},
|
||
error: {
|
||
borderColor: COLORS.error,
|
||
},
|
||
modalOverlay: {
|
||
flex: 1,
|
||
backgroundColor: 'rgba(0,0,0,0.3)',
|
||
justifyContent: 'center',
|
||
paddingHorizontal: 24,
|
||
},
|
||
modalContainer: {
|
||
backgroundColor: COLORS.white,
|
||
borderRadius: 12,
|
||
maxHeight: '70%',
|
||
},
|
||
optionRow: {
|
||
paddingVertical: 16,
|
||
paddingHorizontal: 20,
|
||
borderBottomWidth: 1,
|
||
borderBottomColor: COLORS.gray[200],
|
||
},
|
||
optionText: {
|
||
fontSize: 16,
|
||
color: COLORS.textPrimary,
|
||
},
|
||
});
|
||
|
||
export default SelectInput;
|