Files
beenvoice/apps/mobile/components/ui/Input.tsx
T

123 lines
2.7 KiB
TypeScript

import {
StyleSheet,
Text,
TextInput,
View,
type StyleProp,
type TextInputProps,
type ViewStyle,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useAppTheme } from "@/contexts/ThemeContext";
import { fonts, radii, spacing } from "@/constants/theme";
type InputProps = TextInputProps & {
label: string;
error?: string;
required?: boolean;
leftIcon?: keyof typeof Ionicons.glyphMap;
labelAccessory?: React.ReactNode;
hint?: string;
containerStyle?: StyleProp<ViewStyle>;
};
export function Input({
label,
error,
required,
leftIcon,
labelAccessory,
hint,
containerStyle,
style,
...props
}: InputProps) {
const { colors } = useAppTheme();
return (
<View style={[styles.wrapper, containerStyle]}>
<View style={styles.labelRow}>
<Text style={[styles.label, { color: colors.foreground }]}>
{label}
{required ? <Text style={{ color: colors.destructive }}> *</Text> : null}
</Text>
{labelAccessory}
</View>
<View style={styles.field}>
{leftIcon ? (
<Ionicons
name={leftIcon}
size={16}
color={colors.mutedForeground}
style={styles.leftIcon}
/>
) : null}
<TextInput
placeholderTextColor={colors.mutedForeground}
style={[
styles.input,
leftIcon && styles.inputWithIcon,
{
borderColor: colors.border,
color: colors.foreground,
backgroundColor: colors.cardGlass,
},
error && { borderColor: colors.destructive },
style,
]}
{...props}
/>
</View>
{hint && !error ? (
<Text style={[styles.hint, { color: colors.mutedForeground }]}>{hint}</Text>
) : null}
{error ? <Text style={[styles.error, { color: colors.destructive }]}>{error}</Text> : null}
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
gap: spacing.sm,
},
labelRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: spacing.sm,
},
label: {
fontSize: 14,
fontFamily: fonts.bodyMedium,
},
field: {
position: "relative",
justifyContent: "center",
},
leftIcon: {
position: "absolute",
left: spacing.md,
zIndex: 1,
},
input: {
minHeight: 44,
borderWidth: 1,
borderRadius: radii.md,
paddingHorizontal: spacing.md,
fontSize: 14,
fontFamily: fonts.body,
},
inputWithIcon: {
paddingLeft: spacing.md + 24,
},
hint: {
fontSize: 12,
fontFamily: fonts.body,
},
error: {
fontSize: 13,
fontFamily: fonts.body,
},
});