eletrotupi / tcc/ commit / ae44a25

frontend/components: wrap a button component and input

Stoled from an old project hihi
Pedro Lucas Porcellis porcellis@eletrotupi.com · 6 months ago ae44a253c2bb4d5eb651b570bfad2eed02f26360 Browse files
Parents fb4c3b9
Changed files
4
Additions
+269
Deletions
-6
/dev/null → frontend/components/ui/Button.tsx
1 +
import React from 'react';
2 +
import {
3 +
  TouchableOpacity,
4 +
  Text,
5 +
  StyleSheet,
6 +
  ActivityIndicator,
7 +
  TouchableOpacityProps,
8 +
  ViewStyle,
9 +
  TextStyle,
10 +
} from 'react-native';
11 +
import { useThemeColor } from '@/hooks/use-theme-color';
12 +

13 +
interface ButtonProps extends TouchableOpacityProps {
14 +
  title: string;
15 +
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
16 +
  size?: 'small' | 'medium' | 'large';
17 +
  loading?: boolean;
18 +
  disabled?: boolean;
19 +
  style?: ViewStyle;
20 +
  textStyle?: TextStyle;
21 +
}
22 +

23 +
export const Button: React.FC<ButtonProps> = ({
24 +
  title,
25 +
  variant = 'primary',
26 +
  size = 'medium',
27 +
  loading = false,
28 +
  disabled = false,
29 +
  style,
30 +
  textStyle,
31 +
  onPress,
32 +
  ...props
33 +
}) => {
34 +
  const primaryColor = useThemeColor({ light: '#0a7ea4', dark: '#4fb3d1' }, 'tint');
35 +
  const textColor = useThemeColor({}, 'text');
36 +
  const backgroundColor = useThemeColor({}, 'background');
37 +
  const borderColor = useThemeColor({ light: '#e0e0e0', dark: '#404040' }, 'border');
38 +

39 +
  const getBackgroundColor = () => {
40 +
    if (disabled) return useThemeColor({ light: '#e5e5e5', dark: '#404040' }, 'background');
41 +
    switch (variant) {
42 +
      case 'primary':
43 +
        return primaryColor;
44 +
      case 'secondary':
45 +
        return useThemeColor({ light: '#f3f4f6', dark: '#374151' }, 'background');
46 +
      case 'outline':
47 +
      case 'ghost':
48 +
        return 'transparent';
49 +
      default:
50 +
        return primaryColor;
51 +
    }
52 +
  };
53 +

54 +
  const getTextColor = () => {
55 +
    if (disabled) return useThemeColor({ light: '#9ca3af', dark: '#6b7280' }, 'text');
56 +
    switch (variant) {
57 +
      case 'primary':
58 +
        return '#fff';
59 +
      case 'secondary':
60 +
      case 'outline':
61 +
      case 'ghost':
62 +
        return textColor;
63 +
      default:
64 +
        return '#fff';
65 +
    }
66 +
  };
67 +

68 +
  const getBorderWidth = () => {
69 +
    return variant === 'outline' ? 1 : 0;
70 +
  };
71 +

72 +
  const getBorderColor = () => {
73 +
    if (variant === 'outline') {
74 +
      return disabled ? borderColor : primaryColor;
75 +
    }
76 +
    return 'transparent';
77 +
  };
78 +

79 +
  const getHeight = () => {
80 +
    switch (size) {
81 +
      case 'small':
82 +
        return 36;
83 +
      case 'medium':
84 +
        return 44;
85 +
      case 'large':
86 +
        return 52;
87 +
      default:
88 +
        return 44;
89 +
    }
90 +
  };
91 +

92 +
  const getFontSize = () => {
93 +
    switch (size) {
94 +
      case 'small':
95 +
        return 14;
96 +
      case 'medium':
97 +
        return 16;
98 +
      case 'large':
99 +
        return 18;
100 +
      default:
101 +
        return 16;
102 +
    }
103 +
  };
104 +

105 +
  return (
106 +
    <TouchableOpacity
107 +
      style={[
108 +
        styles.button,
109 +
        {
110 +
          backgroundColor: getBackgroundColor(),
111 +
          borderWidth: getBorderWidth(),
112 +
          borderColor: getBorderColor(),
113 +
          height: getHeight(),
114 +
          opacity: disabled || loading ? 0.6 : 1,
115 +
        },
116 +
        style,
117 +
      ]}
118 +
      disabled={disabled || loading}
119 +
      onPress={onPress}
120 +
      {...props}
121 +
    >
122 +
      {loading ? (
123 +
        <ActivityIndicator color={getTextColor()} />
124 +
      ) : (
125 +
        <Text
126 +
          style={[
127 +
            styles.text,
128 +
            {
129 +
              color: getTextColor(),
130 +
              fontSize: getFontSize(),
131 +
            },
132 +
            textStyle,
133 +
          ]}
134 +
        >
135 +
          {title}
136 +
        </Text>
137 +
      )}
138 +
    </TouchableOpacity>
139 +
  );
140 +
};
141 +

142 +
const styles = StyleSheet.create({
143 +
  button: {
144 +
    borderRadius: 8,
145 +
    paddingHorizontal: 20,
146 +
    alignItems: 'center',
147 +
    justifyContent: 'center',
148 +
    marginVertical: 8,
149 +
  },
150 +
  text: {
151 +
    fontWeight: '600',
152 +
  },
153 +
});
/dev/null → frontend/components/ui/Input.tsx
1 +
import React 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 +
interface InputProps extends TextInputProps {
15 +
  label?: string;
16 +
  error?: string;
17 +
  type?: 'text' | 'email' | 'password';
18 +
  showPasswordToggle?: boolean;
19 +
}
20 +

21 +
export const Input: React.FC<InputProps> = ({
22 +
  label,
23 +
  error,
24 +
  type = 'text',
25 +
  showPasswordToggle = false,
26 +
  style,
27 +
  ...props
28 +
}) => {
29 +
  const [showPassword, setShowPassword] = React.useState(false);
30 +
  const textColor = useThemeColor({}, 'text');
31 +
  const backgroundColor = useThemeColor({ light: '#f5f5f5', dark: '#2a2a2a' }, 'background');
32 +
  const borderColor = useThemeColor({ light: '#e0e0e0', dark: '#404040' }, 'border');
33 +
  const errorColor = '#ef4444';
34 +
  const placeholderColor = useThemeColor({ light: '#9ca3af', dark: '#6b7280' }, 'placeholder');
35 +

36 +
  const keyboardType = type === 'email' ? 'email-address' : 'default';
37 +
  const secureTextEntry = type === 'password' && !showPassword;
38 +

39 +
  return (
40 +
    <View style={styles.container}>
41 +
      {label && (
42 +
        <Text style={[styles.label, { color: textColor }]}>{label}</Text>
43 +
      )}
44 +
      <View style={styles.inputContainer}>
45 +
        <TextInput
46 +
          style={[
47 +
            styles.input,
48 +
            {
49 +
              color: textColor,
50 +
              backgroundColor,
51 +
              borderColor: error ? errorColor : borderColor,
52 +
            },
53 +
            style,
54 +
          ]}
55 +
          placeholderTextColor={placeholderColor}
56 +
          keyboardType={keyboardType}
57 +
          secureTextEntry={secureTextEntry}
58 +
          autoCapitalize={type === 'email' ? 'none' : props.autoCapitalize}
59 +
          autoCorrect={type === 'email' || type === 'password' ? false : props.autoCorrect}
60 +
          {...props}
61 +
        />
62 +
        {type === 'password' && showPasswordToggle && (
63 +
          <TouchableOpacity
64 +
            style={styles.passwordToggle}
65 +
            onPress={() => setShowPassword(!showPassword)}
66 +
          >
67 +
            <Ionicons
68 +
              name={showPassword ? 'eye-off' : 'eye'}
69 +
              size={22}
70 +
              color={placeholderColor}
71 +
            />
72 +
          </TouchableOpacity>
73 +
        )}
74 +
      </View>
75 +
      {error && (
76 +
        <Text style={[styles.error, { color: errorColor }]}>{error}</Text>
77 +
      )}
78 +
    </View>
79 +
  );
80 +
};
81 +

82 +
const styles = StyleSheet.create({
83 +
  container: {
84 +
    marginBottom: 16,
85 +
  },
86 +
  label: {
87 +
    fontSize: 14,
88 +
    fontWeight: '500',
89 +
    marginBottom: 6,
90 +
  },
91 +
  inputContainer: {
92 +
    position: 'relative',
93 +
  },
94 +
  input: {
95 +
    height: 48,
96 +
    paddingHorizontal: 12,
97 +
    paddingRight: 44,
98 +
    borderWidth: 1,
99 +
    borderRadius: 8,
100 +
    fontSize: 16,
101 +
    ...Platform.select({
102 +
      web: {
103 +
        outlineStyle: 'none',
104 +
      },
105 +
    }),
106 +
  },
107 +
  passwordToggle: {
108 +
    position: 'absolute',
109 +
    right: 12,
110 +
    top: 13,
111 +
  },
112 +
  error: {
113 +
    fontSize: 12,
114 +
    marginTop: 4,
115 +
  },
116 +
});
frontend/components/ui/buttons.tsx → /dev/null
1 -
const PrimaryButton = () => (
2 -
)
3 -

4 -
export {
5 -
  PrimaryButton
6 -
}
/dev/null → frontend/components/ui/index.ts
1 +
export { Button } from './Button';
2 +
export { Input } from './Input';