Instantly share code, notes, and snippets.
Created
January 31, 2024 15:14
-
Save dmathisen/337f6904b9688335e63e27e12a64f383 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