eletrotupi / tcc/ frontend/components/ui/Input.tsx7d2b76a987098caa7e4b365e4d41c0bd0a7a2692
4.7 KB | 190 lines Raw
1
import React, { useState } from 'react';
2
import {
3
  TextInput,
4
  TextInputProps,
5
  StyleSheet,
6
  View,
7
  Text,
8
  TouchableOpacity,
9
  Platform,
10
} from 'react-native';
11
import { useThemeColor } from '@/hooks/use-theme-color';
12
import { Ionicons } from '@expo/vector-icons';
13

14
type InputVariant = 'default' | 'ghost' | 'darkGhost';
15

16
interface BaseInputProps {
17
  label?: string;
18
  error?: string;
19
  variant?: InputVariant;
20
}
21

22
function useInputStyles(variant: InputVariant, error?: string) {
23
  let backgroundColor = null;
24

25
  const textColor = useThemeColor({}, 'text');
26
  const defaultBackgroundColor = useThemeColor({}, 'inputBackgroundColor');
27
  const backgroundDarkGhostColor = useThemeColor({}, 'inputBackgroundDarkGhostColor');
28
  const borderColor = useThemeColor({}, 'inputBorderColor');
29
  const placeholderColor = useThemeColor({}, 'inputPlaceholderColor');
30
  const errorColor = '#ef4444';
31

32
  const isGhost = variant === 'ghost';
33
  const isDarkGhost = variant === 'darkGhost';
34

35
  if (isDarkGhost) {
36
    backgroundColor = backgroundDarkGhostColor;
37
  } else if (isGhost) {
38
    backgroundColor = 'transparent';
39
  } else {
40
    backgroundColor = defaultBackgroundColor;
41
  }
42

43
  const inputStyle = {
44
    color: textColor,
45
    backgroundColor: backgroundColor,
46
    borderColor: error ? errorColor : borderColor,
47
    borderWidth: isGhost || isDarkGhost ? 0 : 1,
48
  };
49

50
  return { textColor, placeholderColor, errorColor, inputStyle };
51
}
52

53
interface InputProps extends TextInputProps, BaseInputProps {
54
  type?: 'text' | 'email' | 'password';
55
  showPasswordToggle?: boolean;
56
}
57

58
export const Input: React.FC<InputProps> = ({
59
  label,
60
  error,
61
  variant = 'default',
62
  type = 'text',
63
  showPasswordToggle = false,
64
  style,
65
  ...props
66
}) => {
67
  const [showPassword, setShowPassword] = useState(false);
68
  const { textColor, placeholderColor, errorColor, inputStyle } = useInputStyles(variant, error);
69

70
  const keyboardType = type === 'email' ? 'email-address' : 'default';
71
  const secureTextEntry = type === 'password' && !showPassword;
72

73
  return (
74
    <View style={styles.container}>
75
      {label && (
76
        <Text style={[styles.label, { color: textColor }]}>{label}</Text>
77
      )}
78
      <View style={styles.inputContainer}>
79
        <TextInput
80
          key={`${type}-${showPassword}`}
81
          style={[styles.input, inputStyle, style]}
82
          placeholderTextColor={placeholderColor}
83
          keyboardType={keyboardType}
84
          secureTextEntry={secureTextEntry}
85
          autoCapitalize={type === 'email' ? 'none' : props.autoCapitalize}
86
          autoCorrect={type === 'email' || type === 'password' ? false : props.autoCorrect}
87
          {...props}
88
        />
89
        {type === 'password' && showPasswordToggle && (
90
          <TouchableOpacity
91
            style={styles.passwordToggle}
92
            onPress={() => setShowPassword((prev) => !prev)}
93
            hitSlop={8}
94
          >
95
            <Ionicons
96
              name={showPassword ? 'eye-off' : 'eye'}
97
              size={22}
98
              color={placeholderColor}
99
            />
100
          </TouchableOpacity>
101
        )}
102
      </View>
103
      {error && (
104
        <Text style={[styles.errorText, { color: errorColor }]}>{error}</Text>
105
      )}
106
    </View>
107
  );
108
};
109

110
interface TextAreaProps extends TextInputProps, BaseInputProps {
111
  minRows?: number;
112
  maxRows?: number;
113
}
114

115
export const TextArea: React.FC<TextAreaProps> = ({
116
  label,
117
  error,
118
  variant = 'default',
119
  minRows = 3,
120
  maxRows,
121
  style,
122
  ...props
123
}) => {
124
  const { textColor, placeholderColor, errorColor, inputStyle } = useInputStyles(variant, error);
125

126
  const minHeight = minRows * 24;
127
  const maxHeight = maxRows ? maxRows * 24 : undefined;
128

129
  return (
130
    <View style={styles.container}>
131
      {label && (
132
        <Text style={[styles.label, { color: textColor }]}>{label}</Text>
133
      )}
134
      <TextInput
135
        style={[
136
          styles.textArea,
137
          inputStyle,
138
          { minHeight, maxHeight },
139
          style,
140
        ]}
141
        placeholderTextColor={placeholderColor}
142
        multiline
143
        textAlignVertical="top"
144
        scrollEnabled={!!maxRows}
145
        {...props}
146
      />
147
      {error && (
148
        <Text style={[styles.errorText, { color: errorColor }]}>{error}</Text>
149
      )}
150
    </View>
151
  );
152
};
153

154
const styles = StyleSheet.create({
155
  container: {
156
    marginBottom: 16,
157
  },
158
  label: {
159
    fontSize: 14,
160
    fontWeight: '500',
161
    marginBottom: 6,
162
  },
163
  inputContainer: {
164
    position: 'relative',
165
  },
166
  input: {
167
    height: 48,
168
    paddingHorizontal: 12,
169
    paddingRight: 44,
170
    borderWidth: 1,
171
    borderRadius: 8,
172
    fontSize: 16,
173
  },
174
  textArea: {
175
    paddingHorizontal: 12,
176
    paddingVertical: 12,
177
    borderWidth: 1,
178
    borderRadius: 8,
179
    fontSize: 16,
180
  },
181
  passwordToggle: {
182
    position: 'absolute',
183
    right: 12,
184
    top: 13,
185
  },
186
  errorText: {
187
    fontSize: 12,
188
    marginTop: 4,
189
  },
190
});