eletrotupi / tcc/ frontend/components/ui/Button.tsxc901c35af9c84b6a29e5185545ced1129f33b8f4
4.5 KB | 208 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 secondaryBackgroundColor = useThemeColor({}, 'secondaryBackground');
47
  const dangerColor = useThemeColor({}, 'danger');
48
  const whiteColor = useThemeColor({}, 'white');
49
  const disabledBackgroundColor = useThemeColor(
50
    { light: '#e5e5e5', dark: '#404040' },
51
    'background',
52
  );
53
  const disabledBorderColor = useThemeColor({}, 'disabled');
54
  const borderDashedColor = useThemeColor({}, 'borderDashed');
55

56
  const getBackgroundColor = () => {
57
    if (disabled) return disabledBackgroundColor;
58
    switch (variant) {
59
      case 'primary':
60
        return primaryColor;
61
      case 'secondary':
62
        return secondaryBackgroundColor;
63
      case 'danger':
64
        return dangerColor;
65
      case 'outline':
66
      case 'ghost':
67
      case 'dashed':
68
        return 'transparent';
69
      default:
70
        return primaryColor;
71
    }
72
  };
73

74
  const getTextColor = () => {
75
    if (disabled) return textColor;
76

77
    switch (variant) {
78
      case 'primary':
79
        return textColor;
80
      case 'secondary':
81
        return whiteColor;
82
      case 'outline':
83
      case 'ghost':
84
        return textColor;
85
      case 'dashed':
86
        return textSecondaryColor;
87
      default:
88
        return '#fff';
89
    }
90
  };
91

92
  const getBorderWidth = () => {
93
    if (variant === 'outline' || variant === 'dashed') {
94
      return 1;
95
    } else {
96
      return 0;
97
    }
98
  };
99

100
  const getBorderColor = () => {
101
    if (variant === 'outline' || variant === 'dashed') {
102
      if (disabled) {
103
        return disabledBorderColor;
104
      } else if (variant === 'dashed') {
105
        return borderDashedColor;
106
      } else {
107
        return primaryColor;
108
      }
109
    }
110

111
    return 'transparent';
112
  };
113

114
  const getBorderStyle = () => {
115
    return variant === 'dashed' ? 'dashed' : 'solid';
116
  }
117

118
  const getHeight = () => {
119
    switch (size) {
120
      case 'small':
121
        return 36;
122
      case 'medium':
123
        return 44;
124
      case 'large':
125
        return 52;
126
      default:
127
        return 44;
128
    }
129
  };
130

131
  const getFontSize = () => {
132
    switch (size) {
133
      case 'small':
134
        return 14;
135
      case 'medium':
136
        return 16;
137
      case 'large':
138
        return 18;
139
      default:
140
        return 16;
141
    }
142
  };
143

144
  const getLineHeight = () => {
145
    switch (size) {
146
      case 'small':
147
        return 18;
148
      case 'medium':
149
        return 20;
150
      case 'large':
151
        return 22;
152
      default:
153
        return 20;
154
    }
155
  };
156

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

197
const styles = StyleSheet.create({
198
  button: {
199
    borderRadius: 8,
200
    paddingHorizontal: 20,
201
    alignItems: 'center',
202
    justifyContent: 'center',
203
    marginVertical: 8,
204
  },
205
  text: {
206
    fontWeight: '600',
207
  },
208
});