import { StyleSheet, type ViewStyle } from 'react-native'; import color from 'color'; import { black, white } from '../../styles/themes/v2/colors'; import type { InternalTheme } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; export type ButtonMode = | 'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal'; type BaseProps = { isMode: (mode: ButtonMode) => boolean; theme: InternalTheme; disabled?: boolean; }; const isDark = ({ dark, backgroundColor, }: { dark?: boolean; backgroundColor?: string; }) => { if (typeof dark === 'boolean') { return dark; } if (backgroundColor === 'transparent') { return false; } if (backgroundColor !== 'transparent') { return !color(backgroundColor).isLight(); } return false; }; const getButtonBackgroundColor = ({ isMode, theme, disabled, customButtonColor, }: BaseProps & { customButtonColor?: string; }) => { if (customButtonColor && !disabled) { return customButtonColor; } if (theme.isV3) { if (disabled) { if (isMode('outlined') || isMode('text')) { return 'transparent'; } return theme.colors.surfaceDisabled; } if (isMode('elevated')) { return theme.colors.elevation.level1; } if (isMode('contained')) { return theme.colors.primary; } if (isMode('contained-tonal')) { return theme.colors.secondaryContainer; } } if (isMode('contained')) { if (disabled) { return color(theme.dark ? white : black) .alpha(0.12) .rgb() .string(); } return theme.colors.primary; } return 'transparent'; }; const getButtonTextColor = ({ isMode, theme, disabled, customTextColor, backgroundColor, dark, }: BaseProps & { customTextColor?: string; backgroundColor: string; dark?: boolean; }) => { if (customTextColor && !disabled) { return customTextColor; } if (theme.isV3) { if (disabled) { return theme.colors.onSurfaceDisabled; } if (typeof dark === 'boolean') { if ( isMode('contained') || isMode('contained-tonal') || isMode('elevated') ) { return isDark({ dark, backgroundColor }) ? white : black; } } if (isMode('outlined') || isMode('text') || isMode('elevated')) { return theme.colors.primary; } if (isMode('contained')) { return theme.colors.onPrimary; } if (isMode('contained-tonal')) { return theme.colors.onSecondaryContainer; } } if (disabled) { return color(theme.dark ? white : black) .alpha(0.32) .rgb() .string(); } if (isMode('contained')) { return isDark({ dark, backgroundColor }) ? white : black; } return theme.colors.primary; }; const getButtonBorderColor = ({ isMode, disabled, theme }: BaseProps) => { if (theme.isV3) { if (disabled && isMode('outlined')) { return theme.colors.surfaceDisabled; } if (isMode('outlined')) { return theme.colors.outline; } } if (isMode('outlined')) { return color(theme.dark ? white : black) .alpha(0.29) .rgb() .string(); } return 'transparent'; }; const getButtonBorderWidth = ({ isMode, theme, }: Omit) => { if (theme.isV3) { if (isMode('outlined')) { return 1; } } if (isMode('outlined')) { return StyleSheet.hairlineWidth; } return 0; }; export const getButtonColors = ({ theme, mode, customButtonColor, customTextColor, disabled, dark, }: { theme: InternalTheme; mode: ButtonMode; customButtonColor?: string; customTextColor?: string; disabled?: boolean; dark?: boolean; }) => { const isMode = (modeToCompare: ButtonMode) => { return mode === modeToCompare; }; const backgroundColor = getButtonBackgroundColor({ isMode, theme, disabled, customButtonColor, }); const textColor = getButtonTextColor({ isMode, theme, disabled, customTextColor, backgroundColor, dark, }); const borderColor = getButtonBorderColor({ isMode, theme, disabled }); const borderWidth = getButtonBorderWidth({ isMode, theme }); return { backgroundColor, borderColor, textColor, borderWidth, }; }; type ViewStyleBorderRadiusStyles = Partial< Pick< ViewStyle, | 'borderBottomEndRadius' | 'borderBottomLeftRadius' | 'borderBottomRightRadius' | 'borderBottomStartRadius' | 'borderTopEndRadius' | 'borderTopLeftRadius' | 'borderTopRightRadius' | 'borderTopStartRadius' | 'borderRadius' > >; export const getButtonTouchableRippleStyle = ( style?: ViewStyle, borderWidth: number = 0 ): ViewStyleBorderRadiusStyles => { if (!style) return {}; const touchableRippleStyle: ViewStyleBorderRadiusStyles = {}; const [, borderRadiusStyles] = splitStyles( style, (style) => style.startsWith('border') && style.endsWith('Radius') ); ( Object.keys(borderRadiusStyles) as Array ).forEach((key) => { const value = style[key as keyof ViewStyleBorderRadiusStyles]; if (typeof value === 'number') { // Only subtract borderWidth if value is greater than 0 const radius = value > 0 ? value - borderWidth : 0; touchableRippleStyle[key as keyof ViewStyleBorderRadiusStyles] = radius; } }); return touchableRippleStyle; };