Intial commit
This commit is contained in:
54
src/components/OTPTextInput.tsx
Normal file
54
src/components/OTPTextInput.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import React, {forwardRef} from 'react';
|
||||
import {TextInput, StyleSheet, View} from 'react-native';
|
||||
import Colors from '../../assets/styles/Colors';
|
||||
import FontFamily from '../../assets/styles/FontFamily';
|
||||
|
||||
const OTPTextInput = forwardRef((props: any, ref: any) => {
|
||||
const {value, onChangeText, onKeyPress} = props;
|
||||
|
||||
const handleChange = (text: string) => {
|
||||
if (/^\d?$/.test(text)) {
|
||||
onChangeText(text);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.box}>
|
||||
<TextInput
|
||||
ref={ref}
|
||||
value={value}
|
||||
onChangeText={handleChange}
|
||||
onKeyPress={onKeyPress}
|
||||
style={styles.input}
|
||||
keyboardType="number-pad"
|
||||
maxLength={1}
|
||||
selectionColor={Colors.primary30.color}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.primary60.color,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
input: {
|
||||
fontSize: 16,
|
||||
color: 'black',
|
||||
textAlign: 'center',
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...FontFamily.notoSansBold,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default OTPTextInput;
|
217
src/components/TextInput.tsx
Normal file
217
src/components/TextInput.tsx
Normal file
@ -0,0 +1,217 @@
|
||||
import * as React from 'react';
|
||||
import {Platform, Pressable, StyleSheet, Text, View} from 'react-native';
|
||||
import {TextInput} from 'react-native-paper';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import Colors from '../../assets/styles/Colors';
|
||||
import FontFamily from '../../assets/styles/FontFamily';
|
||||
import DateTimePicker from '@react-native-community/datetimepicker';
|
||||
import {useState} from 'react';
|
||||
import {Dropdown} from 'react-native-element-dropdown';
|
||||
|
||||
interface TextInputComponentProps {
|
||||
title?: string;
|
||||
placeholder?: string;
|
||||
isPassword?: boolean;
|
||||
isRequired?: boolean;
|
||||
isDate?: boolean;
|
||||
isDropdown?: boolean;
|
||||
}
|
||||
|
||||
const genderData = [
|
||||
{label: 'Laki-Laki', value: '1'},
|
||||
{label: 'Perempuan', value: '2'},
|
||||
];
|
||||
|
||||
const TextInputComponent: React.FC<TextInputComponentProps> = ({
|
||||
title,
|
||||
placeholder,
|
||||
isPassword = false,
|
||||
isRequired = false,
|
||||
isDate = false,
|
||||
isDropdown = false,
|
||||
}) => {
|
||||
const [secureText, setSecureText] = useState(isPassword);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
|
||||
const [formattedDate, setFormattedDate] = useState<string>('');
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const [genderValue, setGenderValue] = useState(null);
|
||||
|
||||
const renderGenderItem = (item: any) => {
|
||||
return (
|
||||
<View style={styles.genderItem}>
|
||||
<Text style={styles.genderTextItem}>{item.label}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const handleDateChange = (event: any, date?: Date) => {
|
||||
if (event.type === 'dismissed') {
|
||||
setShowPicker(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (date) {
|
||||
setShowPicker(Platform.OS === 'ios');
|
||||
setSelectedDate(date);
|
||||
const formatted = `${String(date.getDate()).padStart(2, '0')}/${String(
|
||||
date.getMonth() + 1,
|
||||
).padStart(2, '0')}/${date.getFullYear()}`;
|
||||
setFormattedDate(formatted);
|
||||
}
|
||||
};
|
||||
|
||||
const renderInput = () => {
|
||||
if (isDropdown) {
|
||||
return (
|
||||
<View>
|
||||
{title && (
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
{isRequired && <Text style={styles.required}>*</Text>}
|
||||
</View>
|
||||
)}
|
||||
<Dropdown
|
||||
style={styles.dropdown}
|
||||
placeholderStyle={styles.placeholderDropdownStyle}
|
||||
selectedTextStyle={styles.selectedTextStyle}
|
||||
iconStyle={styles.iconStyle}
|
||||
data={genderData}
|
||||
maxHeight={300}
|
||||
labelField="label"
|
||||
valueField="value"
|
||||
placeholder="Jenis Kelamin"
|
||||
value={genderValue}
|
||||
onChange={item => {
|
||||
setGenderValue(item.value);
|
||||
}}
|
||||
renderRightIcon={() => <Icon name="arrow-drop-down" size={20} />}
|
||||
renderItem={renderGenderItem}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDate) {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.titleContainer}>
|
||||
{title && <Text style={styles.title}>{title}</Text>}
|
||||
{isRequired && <Text style={styles.required}>*</Text>}
|
||||
</View>
|
||||
<Pressable onPress={() => setShowPicker(true)}>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
placeholder={placeholder}
|
||||
style={[styles.containerBackground, styles.placeholderText]}
|
||||
theme={{roundness: 12}}
|
||||
placeholderTextColor={Colors.primary60.color}
|
||||
editable={false}
|
||||
value={formattedDate}
|
||||
right={<TextInput.Icon icon="calendar" />}
|
||||
multiline={false}
|
||||
/>
|
||||
</Pressable>
|
||||
{showPicker && (
|
||||
<DateTimePicker
|
||||
value={selectedDate || new Date()}
|
||||
mode="date"
|
||||
display="calendar"
|
||||
onChange={handleDateChange}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{title && (
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
{isRequired && <Text style={styles.required}>*</Text>}
|
||||
</View>
|
||||
)}
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
placeholder={placeholder}
|
||||
style={[styles.containerBackground, styles.placeholderText]}
|
||||
theme={{roundness: 12}}
|
||||
placeholderTextColor={Colors.primary60.color}
|
||||
activeOutlineColor={Colors.primary10.color}
|
||||
secureTextEntry={secureText}
|
||||
right={
|
||||
isPassword ? (
|
||||
<TextInput.Icon
|
||||
icon={secureText ? 'eye-off' : 'eye'}
|
||||
onPress={() => setSecureText(prev => !prev)}
|
||||
forceTextInputFocus={false}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
multiline={false}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return <View>{renderInput()}</View>;
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
containerBackground: {
|
||||
backgroundColor: Colors.neutral100.color,
|
||||
marginTop: 8,
|
||||
},
|
||||
title: {
|
||||
...FontFamily.notoSansBold,
|
||||
fontSize: 12,
|
||||
},
|
||||
titleContainer: {
|
||||
flexDirection: 'row',
|
||||
gap: 5,
|
||||
},
|
||||
required: {
|
||||
...FontFamily.notoSansBold,
|
||||
fontSize: 12,
|
||||
color: Colors.indicatorRed.color,
|
||||
},
|
||||
placeholderText: {
|
||||
fontSize: 13,
|
||||
...FontFamily.notoSansRegular,
|
||||
},
|
||||
genderItem: {
|
||||
padding: 16,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
genderTextItem: {
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
},
|
||||
dropdown: {
|
||||
marginTop: 8,
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 12,
|
||||
paddingVertical: 16,
|
||||
paddingStart: 16,
|
||||
paddingEnd: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.primary40.color,
|
||||
},
|
||||
placeholderDropdownStyle: {
|
||||
fontSize: 13,
|
||||
...FontFamily.notoSansRegular,
|
||||
color: Colors.primary60.color,
|
||||
},
|
||||
selectedTextStyle: {
|
||||
fontSize: 13,
|
||||
...FontFamily.notoSansRegular,
|
||||
},
|
||||
iconStyle: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default TextInputComponent;
|
Reference in New Issue
Block a user