aboutsummaryrefslogtreecommitdiff
path: root/src/components/ui/input.tsx
blob: 25728df131d6777034ccdf20b393cd891ebd5b08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import React, { forwardRef, useState } from 'react';
import {
    StyleSheet,
    Text,
    TextInput,
    type TextInputProps,
    View,
} from 'react-native';
import { Colors, Radius, Spacing, Typography } from '@/constants/theme';

interface InputProps extends TextInputProps {
    label: string;
    error?: string;
    hint?: string;
    prefix?: string;
}

export const Input = forwardRef<TextInput, InputProps>(function Input(
    { label, error, hint, prefix, style, ...rest },
    ref,
) {
    const [focused, setFocused] = useState(false);

    return (
        <View style={styles.wrapper}>
            <Text style={styles.label}>{label}</Text>
            <View
                style={[
                    styles.inputRow,
                    focused && styles.inputRowFocused,
                    !!error && styles.inputRowError,
                ]}
            >
                {prefix ? <Text style={styles.prefix}>{prefix}</Text> : null}
                <TextInput
                    ref={ref}
                    style={[styles.input, style]}
                    placeholderTextColor={Colors.textMuted}
                    onFocus={() => setFocused(true)}
                    onBlur={() => setFocused(false)}
                    selectionColor={Colors.primaryLight}
                    {...rest}
                />
            </View>
            {error ? (
                <Text style={styles.error}>{error}</Text>
            ) : hint ? (
                <Text style={styles.hint}>{hint}</Text>
            ) : null}
        </View>
    );
});

const styles = StyleSheet.create({
    wrapper: {
        gap: Spacing.xs,
    },
    label: {
        ...Typography.caption,
        color: Colors.textSecondary,
        textTransform: 'uppercase',
        letterSpacing: 1,
    },
    inputRow: {
        flexDirection: 'row',
        alignItems: 'center',
        backgroundColor: Colors.surfaceElevated,
        borderRadius: Radius.md,
        borderWidth: 1,
        borderColor: Colors.border,
        paddingHorizontal: Spacing.md,
        height: 52,
    },
    inputRowFocused: {
        borderColor: Colors.primaryLight,
    },
    inputRowError: {
        borderColor: Colors.negative,
    },
    prefix: {
        ...Typography.bodyLarge,
        color: Colors.textSecondary,
        marginRight: Spacing.sm,
    },
    input: {
        flex: 1,
        ...Typography.bodyLarge,
        color: Colors.textPrimary,
    },
    error: {
        ...Typography.caption,
        color: Colors.negative,
    },
    hint: {
        ...Typography.caption,
        color: Colors.textMuted,
    },
});