eletrotupi / tcc/ frontend/components/ui/Button.tsx96b6b65fa3a441d97c317cf1c2ee53661ad5b664
4.4 KB | 202 lines Raw
1
import React from 'react';
2
import {
3
  TouchableOpacity,
4
  StyleSheet,
5
  ActivityIndicator,
6
  TouchableOpacityProps,
7
  ViewStyle,
8
  TextStyle,
9
} from 'react-native';
10
import { Text } from '@/components/ui/Text';
11
import { useThemeColor } from '@/hooks/use-theme-color';
12

13
function toTestId(title: string): string {
14
  return title
15
    .normalize('NFD')
16
    .replace(/[\u0300-\u036f]/g, '')
17
    .toLowerCase()
18
    .replace(/[^a-z0-9]+/g, '-')
19
    .replace(/^-+|-+$/g, '');
20
}
21

22
interface ButtonProps extends TouchableOpacityProps {
23
  title: string;
24
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'dashed' | 'danger';
25
  size?: 'small' | 'medium' | 'large';
26
  loading?: boolean;
27
  disabled?: boolean;
28
  style?: ViewStyle;
29
  textStyle?: TextStyle;
30
}
31

32
export const Button: React.FC<ButtonProps> = ({
33
  title,
34
  variant = 'primary',
35
  size = 'medium',
36
  loading = false,
37
  disabled = false,
38
  style,
39
  textStyle,
40
  onPress,
41
  ...props
42
}) => {
43
  const primaryColor = useThemeColor({}, 'tint');
44
  const textColor = useThemeColor({}, 'text');
45
  const textSecondaryColor = useThemeColor({}, 'textSecondary');
46
  const backgroundColor = useThemeColor({}, 'background');
47
  const borderColor = useThemeColor({}, 'border');
48
  const borderDashedColor = useThemeColor({}, 'borderDashed');
49

50
  const getBackgroundColor = () => {
51
    if (disabled) return useThemeColor({ light: '#e5e5e5', dark: '#404040' }, 'background');
52
    switch (variant) {
53
      case 'primary':
54
        return primaryColor;
55
      case 'secondary':
56
        return useThemeColor({}, 'secondaryBackground');
57
      case 'danger':
58
        return useThemeColor({}, 'danger');
59
      case 'outline':
60
      case 'ghost':
61
      case 'dashed':
62
        return 'transparent';
63
      default:
64
        return primaryColor;
65
    }
66
  };
67

68
  const getTextColor = () => {
69
    if (disabled) return useThemeColor({}, 'text');
70

71
    switch (variant) {
72
      case 'primary':
73
        return textColor;
74
      case 'secondary':
75
        return useThemeColor({}, 'white');
76
      case 'outline':
77
      case 'ghost':
78
        return textColor;
79
      case 'dashed':
80
        return textSecondaryColor;
81
      default:
82
        return '#fff';
83
    }
84
  };
85

86
  const getBorderWidth = () => {
87
    if (variant === 'outline' || variant === 'dashed') {
88
      return 1;
89
    } else {
90
      return 0;
91
    }
92
  };
93

94
  const getBorderColor = () => {
95
    if (variant === 'outline' || variant === 'dashed') {
96
      if (disabled) {
97
        return borderColor;
98
      } else if (variant === 'dashed') {
99
        return borderDashedColor;
100
      } else {
101
        return primaryColor;
102
      }
103
    }
104

105
    return 'transparent';
106
  };
107

108
  const getBorderStyle = () => {
109
    return variant === 'dashed' ? 'dashed' : 'solid';
110
  }
111

112
  const getHeight = () => {
113
    switch (size) {
114
      case 'small':
115
        return 36;
116
      case 'medium':
117
        return 44;
118
      case 'large':
119
        return 52;
120
      default:
121
        return 44;
122
    }
123
  };
124

125
  const getFontSize = () => {
126
    switch (size) {
127
      case 'small':
128
        return 14;
129
      case 'medium':
130
        return 16;
131
      case 'large':
132
        return 18;
133
      default:
134
        return 16;
135
    }
136
  };
137

138
  const getLineHeight = () => {
139
    switch (size) {
140
      case 'small':
141
        return 18;
142
      case 'medium':
143
        return 20;
144
      case 'large':
145
        return 22;
146
      default:
147
        return 20;
148
    }
149
  };
150

151
  return (
152
    <TouchableOpacity
153
      testID={props.testID ?? `button-${toTestId(title)}`}
154
      style={[
155
        styles.button,
156
        {
157
          backgroundColor: getBackgroundColor(),
158
          borderWidth: getBorderWidth(),
159
          borderColor: getBorderColor(),
160
          borderStyle: getBorderStyle(),
161
          height: getHeight(),
162
          opacity: disabled || loading ? 0.6 : 1,
163
        },
164
        style,
165
      ]}
166
      disabled={disabled || loading}
167
      onPress={onPress}
168
      {...props}
169
    >
170
      {loading ? (
171
        <ActivityIndicator color={getTextColor()} />
172
      ) : (
173
        <Text
174
          style={[
175
            styles.text,
176
            {
177
              color: getTextColor(),
178
              fontSize: getFontSize(),
179
              lineHeight: getLineHeight(),
180
            },
181
            textStyle,
182
          ]}
183
        >
184
          {title}
185
        </Text>
186
      )}
187
    </TouchableOpacity>
188
  );
189
};
190

191
const styles = StyleSheet.create({
192
  button: {
193
    borderRadius: 8,
194
    paddingHorizontal: 20,
195
    alignItems: 'center',
196
    justifyContent: 'center',
197
    marginVertical: 8,
198
  },
199
  text: {
200
    fontWeight: '600',
201
  },
202
});