first commit

This commit is contained in:
2025-06-23 10:18:59 +08:00
parent 20d2053276
commit ba2dc1211f
32 changed files with 15692 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
// components/TextInputCustom.tsx
import Ionicons from "@expo/vector-icons/Ionicons";
import React, { useState } from "react";
import {
TextInput as RNTextInput,
StyleProp,
Text,
TouchableOpacity,
View,
ViewStyle,
} from "react-native";
import { textInputStyles } from "./textInputStyles";
type IconType = React.ReactNode | string;
type Props = {
iconLeft?: IconType;
iconRight?: IconType;
label?: string;
required?: boolean;
error?: string;
secureTextEntry?: boolean;
fontColor?: string;
disabled?: boolean;
borderRadius?: number;
style?: StyleProp<ViewStyle>;
} & Omit<React.ComponentProps<typeof RNTextInput>, "style">;
export const TextInputCustom = ({
iconLeft,
iconRight,
label,
required = false,
error = "",
secureTextEntry = false,
fontColor = "#000",
disabled = false,
borderRadius = 8,
style,
...rest
}: Props) => {
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
// Helper untuk render ikon
const renderIcon = (icon: IconType) => {
if (!icon) return null;
return typeof icon === "string" ? (
<Text style={textInputStyles.iconText}>{icon}</Text>
) : (
icon
);
};
return (
<View style={textInputStyles.container}>
{label && (
<Text style={textInputStyles.label}>
{label}
{required && <Text style={textInputStyles.required}> *</Text>}
</Text>
)}
<View
style={[
textInputStyles.inputContainer,
disabled && textInputStyles.disabled,
{ borderRadius },
error ? textInputStyles.errorBorder : null,
style,
]}
>
{iconLeft && (
<View style={textInputStyles.icon}>{renderIcon(iconLeft)}</View>
)}
<RNTextInput
style={[textInputStyles.input, { color: fontColor }]}
editable={!disabled}
secureTextEntry={secureTextEntry && !isPasswordVisible}
{...rest}
/>
{secureTextEntry && (
<TouchableOpacity
onPress={() => setIsPasswordVisible((prev) => !prev)}
style={textInputStyles.icon}
>
<Ionicons
name={isPasswordVisible ? "eye-off" : "eye"}
size={20}
color="#888"
/>
</TouchableOpacity>
)}
{iconRight && (
<View style={textInputStyles.icon}>{renderIcon(iconRight)}</View>
)}
</View>
{error ? <Text style={textInputStyles.errorMessage}>{error}</Text> : null}
</View>
);
};

View File

@@ -0,0 +1,71 @@
// components/text-input.styles.ts
import { AccentColor, MainColor } from "@/constants/color-palet";
import { StyleSheet } from "react-native";
export const textInputStyles = StyleSheet.create({
// Container utama input (View luar)
container: {
marginBottom: 16,
},
// Label di atas input
label: {
fontSize: 14,
marginBottom: 6,
fontWeight: "500",
color: MainColor.white,
},
// Tanda bintang merah untuk required
required: {
color: "red",
},
// Pesan error di bawah input
errorMessage: {
marginTop: 4,
fontSize: 12,
color: MainColor.red,
},
// Wrapper input (View pembungkus TextInput)
inputContainer: {
flexDirection: "row",
alignItems: "center",
borderWidth: 1,
borderColor: AccentColor.white,
backgroundColor: MainColor.login,
paddingHorizontal: 12,
height: 50,
},
// Style saat disabled
disabled: {
backgroundColor: "#f9f9f9",
borderColor: "#e0e0e0",
},
// Input utama (TextInput)
input: {
flex: 1,
fontSize: 16,
paddingVertical: 0,
},
// Ikon di kiri/kanan
icon: {
marginHorizontal: 4,
justifyContent: "center",
},
// Teks ikon jika berupa string
iconText: {
fontSize: 16,
color: "#000",
},
// Border merah jika ada error
errorBorder: {
borderColor: "red",
},
});