xref: /expo/home/components/Button.tsx (revision 896a29e5)
1import { borderRadius } from '@expo/styleguide-native';
2import { ExpoTheme, scale, Text, useExpoTheme } from 'expo-dev-client-components';
3import React from 'react';
4import { ActivityIndicator, TouchableOpacity, ViewStyle } from 'react-native';
5
6type Theme = 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'error';
7
8type Props = {
9  label: string;
10  onPress: () => void;
11  theme?: Theme;
12  disabled?: boolean;
13  loading?: boolean;
14  style?: ViewStyle;
15};
16
17function getThemeColors(
18  theme: Theme,
19  expoTheme: ExpoTheme
20): { backgroundColor: string; borderColor?: string; borderWidth?: 1; color: string } {
21  switch (theme) {
22    case 'primary':
23      return {
24        backgroundColor: expoTheme.button.primary.background,
25        color: expoTheme.button.primary.foreground,
26      };
27    case 'secondary':
28      return {
29        backgroundColor: expoTheme.button.secondary.background,
30        color: expoTheme.button.secondary.foreground,
31      };
32    case 'tertiary':
33      return {
34        backgroundColor: expoTheme.button.tertiary.background,
35        color: expoTheme.button.tertiary.foreground,
36      };
37    case 'ghost':
38      return {
39        backgroundColor: expoTheme.button.ghost.background,
40        color: expoTheme.button.ghost.foreground,
41        borderColor: expoTheme.button.ghost.border,
42        borderWidth: 1,
43      };
44    case 'error':
45      return {
46        backgroundColor: expoTheme.background.error,
47        color: expoTheme.text.error,
48        borderColor: expoTheme.border.error,
49        borderWidth: 1,
50      };
51  }
52}
53
54export function Button({ label, theme = 'tertiary', onPress, loading, disabled, style }: Props) {
55  const expoTheme = useExpoTheme();
56
57  const { backgroundColor, borderColor, borderWidth, color } = getThemeColors(theme, expoTheme);
58
59  return (
60    <TouchableOpacity
61      style={[
62        {
63          backgroundColor,
64          borderRadius: borderRadius.medium,
65          paddingVertical: scale.small,
66          borderColor,
67          borderWidth,
68          paddingHorizontal: scale.medium,
69          opacity: disabled ? 0.5 : 1,
70        },
71        style,
72      ]}
73      disabled={disabled}
74      onPress={onPress}>
75      {loading ? (
76        <ActivityIndicator />
77      ) : (
78        <Text type="InterSemiBold" style={{ color }}>
79          {label}
80        </Text>
81      )}
82    </TouchableOpacity>
83  );
84}
85