Instantly share code, notes, and snippets.
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save filippobarcellos/0d6e59a063b839840faebd2fc686c622 to your computer and use it in GitHub Desktop.
React Native generic button component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { | |
Pressable, | |
PressableProps, | |
StyleProp, | |
StyleSheet, | |
Text, | |
TextStyle, | |
ViewStyle, | |
useColorScheme | |
} from 'react-native'; | |
import { Icon } from '@/components/common/icon'; | |
import colors from '@/styles/colors'; | |
// Props | |
type ButtonTypes = 'contained' | 'outlined' | 'text'; | |
type ButtonSizes = 'xs' | 'sm' | 'md' | 'lg'; | |
type ButtonProps = PressableProps & { | |
title?: string; | |
icon?: string; | |
type?: ButtonTypes; | |
size?: ButtonSizes; | |
buttonStyle?: ViewStyle; | |
textStyle?: TextStyle; | |
}; | |
// Sizes | |
type SizeProps = { fontSize: number; iconSize: number; padV: number; padH: number }; | |
const sizes: Record<ButtonSizes, SizeProps> = { | |
xs: { fontSize: 12, iconSize: 18, padV: 3, padH: 4 }, | |
sm: { fontSize: 14, iconSize: 22, padV: 6, padH: 9 }, | |
md: { fontSize: 16, iconSize: 24, padV: 12, padH: 18 }, | |
lg: { fontSize: 20, iconSize: 28, padV: 24, padH: 36 } | |
}; | |
export const Button = (props: ButtonProps) => { | |
const { onPress, title, icon, type = 'contained', size = 'md', buttonStyle, textStyle } = props; | |
// Theme | |
const isLightTheme = useColorScheme() === 'light'; | |
const themedButton = getThemedButton(isLightTheme, type); | |
const themedText = getThemedText(isLightTheme, type); | |
const iconColor = (themedText as TextStyle)?.color?.toString() ?? colors.white; | |
// Sizing | |
const { fontSize, iconSize, padV, padH } = sizes[size]; | |
const padding: ViewStyle = { paddingVertical: padV, paddingHorizontal: padH }; | |
const marginLeft = icon ? 5 : 0; // Only apply margin if it has an icon | |
// Component | |
return ( | |
<Pressable onPress={onPress} style={[styles.button, padding, themedButton, buttonStyle]}> | |
{icon && <Icon name={icon} size={iconSize} color={iconColor} style={textStyle ?? {}} />} | |
{title && <Text style={[{ fontSize, marginLeft }, themedText, textStyle]}>{title}</Text>} | |
</Pressable> | |
); | |
}; | |
// Helpers | |
const getThemedButton = (isLightTheme: boolean, type: ButtonTypes): StyleProp<ViewStyle> => { | |
if (isLightTheme) { | |
if (type === 'outlined') return styles.outlinedLightButton; | |
if (type === 'text') return styles.textLightButton; | |
return styles.containedLightButton; | |
} else { | |
if (type === 'outlined') return styles.outlinedDarkButton; | |
if (type === 'text') return styles.textDarkButton; | |
return styles.containedDarkButton; | |
} | |
}; | |
const getThemedText = (isLightTheme: boolean, type: ButtonTypes): StyleProp<TextStyle> => { | |
if (isLightTheme) { | |
if (type === 'outlined') return styles.outlinedLightText; | |
if (type === 'text') return styles.textLightText; | |
return styles.containedLightText; | |
} else { | |
if (type === 'outlined') return styles.outlinedDarkText; | |
if (type === 'text') return styles.textDarkText; | |
return styles.containedDarkText; | |
} | |
}; | |
// Styles | |
const styles = StyleSheet.create({ | |
// Button | |
button: { | |
flexDirection: 'row', | |
alignItems: 'center', | |
justifyContent: 'center', | |
borderRadius: 4, | |
elevation: 2 | |
}, | |
// Dark Button | |
containedDarkButton: { | |
backgroundColor: colors.primary | |
}, | |
outlinedDarkButton: { | |
backgroundColor: 'transparent', | |
borderWidth: 3, | |
borderColor: colors.primary, | |
elevation: 0 | |
}, | |
textDarkButton: { | |
elevation: 0 | |
}, | |
// Light Button | |
containedLightButton: { | |
backgroundColor: colors.bg2 | |
}, | |
outlinedLightButton: { | |
backgroundColor: 'transparent', | |
borderWidth: 2, | |
borderColor: colors.primary | |
}, | |
textLightButton: { | |
elevation: 0 | |
}, | |
// Dark Text | |
containedDarkText: { | |
color: colors.white | |
}, | |
outlinedDarkText: { | |
color: colors.primary | |
}, | |
textDarkText: { | |
color: colors.primary | |
}, | |
// Light Text | |
containedLightText: { | |
color: colors.bgText | |
}, | |
outlinedLightText: { | |
color: colors.bgText | |
}, | |
textLightText: { | |
color: colors.bgText | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment