import type { ReactNativeStyle } from '@emotion/native'; import styled from '@emotion/native'; import type { Theme } from '@emotion/react'; import { TouchableHighlight, View } from 'react-native'; import { scale } from '../../utils/scale'; import Icon from '../Icon'; import Typography from '../Typography'; import { transformKebabCaseToCamelCase, type CamelCase, } from '../../utils/helpers'; type Intent = 'primary' | 'secondary' | 'danger' | 'white' | 'inverted'; type ThemeVariant = | 'filled-primary' | 'filled-secondary' | 'filled-danger' | 'filled-white' | 'filled-inverted' | 'outlined-primary' | 'outlined-secondary' | 'outlined-danger' | 'outlined-white' | 'outlined-inverted' | 'text-primary' | 'text-secondary' | 'text-danger' | 'text-white' | 'text-inverted'; const getButtonSize = (themeIsCompact: boolean, themeIsMedium?: boolean) => { if (themeIsCompact) return scale(36); if (themeIsMedium) return scale(48); return scale(48); }; const getTextButtonPaddingVertical = ( theme: Theme, themeInlineText: boolean, themeIsCompact: boolean ) => { if (themeInlineText) { return 0; } if (themeIsCompact) { return theme.__hd__.button.space.compact.buttonPaddingVertical; } return theme.__hd__.button.space.default.textButtonPadding; }; const getTextButtonPaddingHorizontal = ( theme: Theme, themeInlineText: boolean, themeIsCompact: boolean ) => { if (themeInlineText) { return 0; } if (themeIsCompact) { return theme.__hd__.button.space.compact.buttonPaddingHorizontal; } return theme.__hd__.button.space.default.textButtonPadding; }; const genDisabledStyles = (disabled: boolean, loading: boolean) => { if (!disabled || loading) return; return { opacity: 0.25 }; }; const genFilledContainerStyles = ( theme: Theme, intent: Capitalize, loading: boolean, compact: boolean, medium: boolean, iconOnly: boolean ): ReactNativeStyle => { const backgroundColorStyling = () => { const colorKey = `filled${intent}` as const; if (loading) { return { backgroundColor: theme.__hd__.button.colors.pressedBackground[colorKey], }; } return { backgroundColor: theme.__hd__.button.colors.background[colorKey], }; }; const paddingStyling = (): ReactNativeStyle => { if (iconOnly) return { alignSelf: 'flex-start', width: getButtonSize(compact, medium), borderRadius: theme.__hd__.button.radii.rounded, }; if (compact) return { alignSelf: 'flex-start', paddingVertical: theme.__hd__.button.space.compact.buttonPaddingVertical, paddingHorizontal: theme.__hd__.button.space.default.buttonPadding, borderRadius: theme.__hd__.button.radii.default, }; if (medium) { return { alignSelf: 'flex-start', borderRadius: theme.__hd__.button.radii.default, paddingHorizontal: theme.__hd__.button.space.medium.buttonPadding, }; } return { alignSelf: 'stretch', paddingHorizontal: theme.__hd__.button.space.default.buttonPadding, borderRadius: theme.__hd__.button.radii.default, }; }; return { height: getButtonSize(compact, medium), flexDirection: 'row', justifyContent: 'center', alignItems: 'center', ...paddingStyling(), ...backgroundColorStyling(), }; }; const genOutlineContainerStyles = ( theme: Theme, intent: Capitalize, loading: boolean, compact: boolean, medium: boolean, iconOnly: boolean ): ReactNativeStyle => { const borderColorStyling = () => { const colorKey = `outlined${intent}` as const; if (loading) { return { borderColor: theme.__hd__.button.colors.border[colorKey], backgroundColor: theme.__hd__.button.colors.pressedBackground[colorKey], }; } return { borderColor: theme.__hd__.button.colors.border[colorKey] }; }; const paddingStyling = (): ReactNativeStyle => { if (iconOnly) return { alignSelf: 'flex-start', width: getButtonSize(compact, medium), borderRadius: theme.__hd__.button.radii.rounded, }; if (compact) return { alignSelf: 'flex-start', paddingVertical: theme.__hd__.button.space.compact.buttonPaddingVertical - theme.__hd__.button.borderWidth.default, paddingHorizontal: theme.__hd__.button.space.default.buttonPadding - theme.__hd__.button.borderWidth.default, borderRadius: theme.__hd__.button.radii.default, }; if (medium) { return { alignSelf: 'flex-start', borderRadius: theme.__hd__.button.radii.default, paddingHorizontal: theme.__hd__.button.space.medium.buttonPaddingHorizontal - theme.__hd__.button.borderWidth.default, }; } return { alignSelf: 'stretch', paddingHorizontal: theme.__hd__.button.space.default.buttonPadding - theme.__hd__.button.borderWidth.default, borderRadius: theme.__hd__.button.radii.default, }; }; return { height: getButtonSize(compact, medium), flexDirection: 'row', justifyContent: 'center', alignItems: 'center', ...paddingStyling(), borderWidth: theme.__hd__.button.borderWidth.default, backgroundColor: 'transparent', ...borderColorStyling(), }; }; const getTextContainerStyles = ( theme: Theme, intent: Capitalize, loading: boolean, inlineText: boolean, compact: boolean, medium: boolean, iconOnly: boolean ): ReactNativeStyle => { const colorKey = `text${intent}` as const; const paddingStyles = (): ReactNativeStyle => { if (iconOnly) { return { alignSelf: 'flex-start', borderRadius: theme.__hd__.button.radii.rounded, width: inlineText ? undefined : getButtonSize(compact, medium), }; } if (compact) { return { alignSelf: 'flex-start', paddingVertical: getTextButtonPaddingVertical( theme, inlineText, compact ), paddingHorizontal: getTextButtonPaddingHorizontal( theme, inlineText, compact ), }; } if (medium) { return { alignSelf: 'flex-start', paddingHorizontal: inlineText ? 0 : theme.__hd__.button.space.medium.textButtonPadding, }; } return { paddingHorizontal: inlineText ? 0 : theme.__hd__.button.space.default.textButtonPadding, }; }; return { height: inlineText ? undefined : getButtonSize(compact, medium), borderRadius: theme.__hd__.button.radii.rounded, flexDirection: 'row', justifyContent: 'center', alignItems: 'center', backgroundColor: !inlineText && loading ? theme.__hd__.button.colors.pressedBackground[colorKey] : 'transparent', borderWidth: 0, ...paddingStyles(), }; }; const genTextStyles = ( theme: Theme, themeButtonVariant: CamelCase, intent?: Capitalize, isPressed?: boolean ): ReactNativeStyle => { if (isPressed && intent) { const colorKey = `inlineText${intent}` as const; return { color: theme.__hd__.button.colors.pressedText[colorKey], }; } return { color: theme.__hd__.button.colors.text[themeButtonVariant], }; }; const StyledButtonContainer = styled(TouchableHighlight)<{ disabled?: boolean; themeButtonVariant: ThemeVariant; loading?: boolean; themeInlineText?: boolean; themeIsCompact?: boolean; themeIsMedium?: boolean; themeIsIconOnly?: boolean; }>( ({ disabled = false, loading = false, themeButtonVariant, themeInlineText = false, theme, themeIsCompact = false, themeIsMedium = false, themeIsIconOnly = false, }) => { switch (themeButtonVariant) { case 'filled-primary': return { ...genFilledContainerStyles( theme, 'Primary', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'filled-secondary': return { ...genFilledContainerStyles( theme, 'Secondary', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'filled-danger': return { ...genFilledContainerStyles( theme, 'Danger', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'filled-white': return { ...genFilledContainerStyles( theme, 'White', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'filled-inverted': return { ...genFilledContainerStyles( theme, 'Inverted', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'outlined-primary': return { ...genOutlineContainerStyles( theme, 'Primary', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'outlined-secondary': return { ...genOutlineContainerStyles( theme, 'Secondary', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'outlined-danger': return { ...genOutlineContainerStyles( theme, 'Danger', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'outlined-white': return { ...genOutlineContainerStyles( theme, 'White', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'outlined-inverted': return { ...genOutlineContainerStyles( theme, 'Inverted', loading, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'text-primary': return { ...getTextContainerStyles( theme, 'Primary', loading, themeInlineText, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'text-secondary': return { ...getTextContainerStyles( theme, 'Secondary', loading, themeInlineText, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'text-danger': return { ...getTextContainerStyles( theme, 'Danger', loading, themeInlineText, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'text-white': return { ...getTextContainerStyles( theme, 'White', loading, themeInlineText, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; case 'text-inverted': return { ...getTextContainerStyles( theme, 'Inverted', loading, themeInlineText, themeIsCompact, themeIsMedium, themeIsIconOnly ), ...genDisabledStyles(disabled, loading), }; } } ); const StyledButtonText = styled(Typography.Title)<{ disabled?: boolean; themeButtonVariant: ThemeVariant; }>(({ themeButtonVariant, theme }) => { const themeStyling = () => { const camelCaseThemeVariant = transformKebabCaseToCamelCase(themeButtonVariant); return genTextStyles(theme, camelCaseThemeVariant); }; return { flexShrink: 1, textAlign: 'center', textAlignVertical: 'center', lineHeight: theme.__hd__.button.lineHeights.buttonText, ...themeStyling(), }; }); const StyledSmallButtonText = styled( StyledButtonText.withComponent(Typography.Body) )(({ theme }) => ({ lineHeight: theme.__hd__.button.lineHeights.utilityButtonText, })); const StyledButtonTitleOfVariantText = styled(Typography.Body)<{ disabled?: boolean; themeButtonVariant: | 'text-primary' | 'text-secondary' | 'text-danger' | 'text-white' | 'text-inverted'; themeIsPressed?: boolean; themeIsCompact: boolean; }>(({ themeButtonVariant, themeIsPressed, theme, themeIsCompact }) => { const themeStyling = () => { const themeVariant = transformKebabCaseToCamelCase(themeButtonVariant); switch (themeButtonVariant) { case 'text-primary': return genTextStyles(theme, themeVariant, 'Primary', themeIsPressed); case 'text-secondary': return genTextStyles(theme, themeVariant, 'Secondary', themeIsPressed); case 'text-danger': return genTextStyles(theme, themeVariant, 'Danger', themeIsPressed); case 'text-white': return genTextStyles(theme, themeVariant, 'White', themeIsPressed); case 'text-inverted': return genTextStyles(theme, themeVariant, 'Inverted', themeIsPressed); } }; return { flexShrink: 1, lineHeight: theme.__hd__.button.lineHeights.titleOfTextVariant[ themeIsCompact ? 'compact' : 'default' ], textAlign: 'center', textAlignVertical: 'center', ...themeStyling(), }; }); const StyledButtonIconWrapper = styled(View)<{ themePosition: 'left' | 'right'; themeIsCompact?: boolean; themeIsIconOnly?: boolean; }>(({ themePosition, theme, themeIsCompact, themeIsIconOnly }) => { if (themeIsIconOnly) return {}; switch (themePosition) { case 'left': return { paddingRight: themeIsCompact ? theme.__hd__.button.space.compact.iconPadding : theme.__hd__.button.space.default.iconPadding, }; case 'right': return { paddingLeft: themeIsCompact ? theme.__hd__.button.space.compact.iconPadding : theme.__hd__.button.space.default.iconPadding, }; } }); const StyledButtonIcon = styled(Icon)<{ disabled?: boolean; themeButtonVariant: ThemeVariant; themeIsPressed?: boolean; themeIsCompact?: boolean; }>(({ themeButtonVariant, themeIsPressed, theme, themeIsCompact }) => { const themeStyling = () => { const themeVariant = transformKebabCaseToCamelCase(themeButtonVariant); switch (themeButtonVariant) { case 'filled-primary': case 'outlined-primary': case 'text-primary': return genTextStyles(theme, themeVariant, 'Primary', themeIsPressed); case 'filled-secondary': case 'outlined-secondary': case 'text-secondary': return genTextStyles(theme, themeVariant, 'Secondary', themeIsPressed); case 'filled-danger': case 'outlined-danger': case 'text-danger': return genTextStyles(theme, themeVariant, 'Danger', themeIsPressed); case 'filled-white': case 'outlined-white': case 'text-white': return genTextStyles(theme, themeVariant, 'White', themeIsPressed); case 'filled-inverted': case 'outlined-inverted': case 'text-inverted': return genTextStyles(theme, themeVariant, 'Inverted', themeIsPressed); } }; return { fontSize: themeIsCompact ? theme.__hd__.button.sizes.iconSize.compact : theme.__hd__.button.sizes.iconSize.default, ...themeStyling(), }; }); export { StyledButtonContainer, StyledButtonIcon, StyledButtonIconWrapper, StyledButtonText, StyledButtonTitleOfVariantText, StyledSmallButtonText, }; export type { Intent, ThemeVariant, CamelCase };