import { TextInput, View, StyleSheet, Animated } from 'react-native'; import styled from '@emotion/native'; import type { Theme } from '@emotion/react'; import Typography from '../Typography'; export type State = 'default' | 'filled' | 'disabled' | 'readonly' | 'error'; type Variant = 'text' | 'textarea'; const genBorderWidth = ( theme: Theme, themeState: State, themeFocused: boolean ): number => { if (themeState === 'readonly') return 0; return themeFocused ? theme.__hd__.textInput.borderWidths.container.focused : theme.__hd__.textInput.borderWidths.container.normal; }; const genBorderColor = ( theme: Theme, themeState: State, themeFocused: boolean, themeFilled: boolean ): string => { if (themeState === 'error' && !themeFilled) { return theme.__hd__.textInput.colors.borders.default; } if (themeState === 'error' && themeFilled) { return theme.__hd__.textInput.colors.borders.error; } if (themeFocused) { return ( theme.__hd__.textInput.colors.borders.focused ?? theme.__hd__.textInput.colors.borders.default ); } return ( theme.__hd__.textInput.colors.borders[themeState] ?? theme.__hd__.textInput.colors.borders.default ); }; const StyledContainer = styled(View)(({ theme }) => ({ width: '100%', marginTop: theme.__hd__.textInput.space.containerMarginTop, })); const StyledLabelContainerInsideTextInput = styled(Animated.View)<{ themeVariant: Variant; }>(({ themeVariant, theme }) => ({ flexDirection: 'row', alignItems: themeVariant === 'text' ? 'center' : 'flex-start', position: 'absolute', zIndex: 1, left: 0, right: theme.space.medium, top: 0, })); const StyledLabelInsideTextInput = styled(View)<{ themeState: State; }>(({ theme, themeState }) => ({ textAlignVertical: 'center', alignContent: 'center', alignItems: 'center', color: theme.__hd__.textInput.colors.labelsInsideTextInput[themeState], marginTop: theme.__hd__.textInput.space.labelInsideTextInputMarginTop, flexDirection: 'row', })); const StyledAsteriskLabelInsideTextInput = styled(Typography.Body)<{ themeState: State; }>(({ theme, themeState }) => ({ color: theme.__hd__.textInput.colors.asterisks[themeState], })); const StyledErrorContainer = styled(View)(({ theme }) => ({ marginRight: theme.__hd__.textInput.space.errorContainerMarginRight, flexDirection: 'row', alignItems: 'center', flex: 1, flexGrow: 4, })); const StyledError = styled(Typography.Caption)(({ theme }) => ({ color: theme.__hd__.textInput.colors.error, })); const StyledMaxLengthMessage = styled(Typography.Caption)<{ themeState: State; }>(({ theme, themeState }) => ({ color: theme.__hd__.textInput.colors.maxLengthLabels[themeState], flex: 1, flexGrow: 1, textAlign: 'right', })); const StyledHelperText = Typography.Caption; const StyledTextInput = styled(TextInput)<{ themeVariant: Variant }>( ({ theme, themeVariant }) => ({ textAlignVertical: themeVariant === 'text' ? 'center' : 'top', fontSize: theme.__hd__.textInput.fontSizes.text, alignSelf: 'stretch', flexGrow: 2, marginHorizontal: 0, paddingVertical: 0, maxHeight: theme.__hd__.textInput.sizes.textInputMaxHeight, height: themeVariant === 'text' ? undefined : theme.__hd__.textInput.sizes.textareaHeight, fontFamily: theme.__hd__.textInput.fonts.text, }) ); const StyledBorderBackDrop = styled(View)<{ themeState: State; themeFocused: boolean; themeFilled: boolean; }>(({ theme, themeFocused, themeState, themeFilled }) => ({ ...StyleSheet.absoluteFillObject, borderWidth: genBorderWidth(theme, themeState, themeFocused), borderRadius: theme.__hd__.textInput.radii.container, borderColor: genBorderColor(theme, themeState, themeFocused, themeFilled), })); const StyledTextInputContainer = styled(View)<{ themeVariant: Variant; themeState: State; }>(({ theme, themeVariant, themeState }) => ({ flexDirection: 'row', alignItems: 'center', paddingHorizontal: theme.__hd__.textInput.space.containerHorizontalPadding, paddingVertical: theme.__hd__.textInput.space.containerVerticalPadding, borderRadius: theme.__hd__.textInput.radii.container, ...(themeVariant === 'text' && { minHeight: theme.__hd__.textInput.sizes.containerMinHeight, }), ...(themeState === 'disabled' && { opacity: 0.5 }), gap: theme.space.smallMedium, })); // Outer wrapper that owns flex-grow/shrink and provides the positioning context // for StyledLabelContainerInsideTextInput (position: absolute). const StyledInputContentContainer = styled(View)<{ themeHasLabel: boolean }>( ({ themeHasLabel }) => ({ flexGrow: 2, flexShrink: 1, alignSelf: 'stretch', justifyContent: themeHasLabel ? 'flex-start' : 'center', }) ); const StyledTextInputAndLabelContainer = styled(View)<{ themeHasLabel: boolean; }>(({ theme, themeHasLabel }) => ({ flexDirection: 'row', alignItems: 'center', alignSelf: 'stretch', ...(themeHasLabel && { paddingTop: theme.__hd__.textInput.space.inputAndLabelContainerPaddingTop, }), })); const StyledErrorAndHelpTextContainer = styled(View)(({ theme }) => ({ minHeight: theme.__hd__.textInput.sizes.errorAndHelpTextContainerMinHeight, paddingTop: theme.__hd__.textInput.space.errorAndHelpTextContainerPaddingTop, })); const StyledErrorAndMaxLengthContainer = styled(View)(({ theme }) => ({ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginTop: theme.__hd__.textInput.space.errorAndHelpTextContainerMarginTop, })); export { StyledTextInputContainer, StyledAsteriskLabelInsideTextInput, StyledTextInput, StyledError, StyledMaxLengthMessage, StyledLabelInsideTextInput, StyledContainer, StyledErrorContainer, StyledHelperText, StyledInputContentContainer, StyledTextInputAndLabelContainer, StyledLabelContainerInsideTextInput, StyledErrorAndHelpTextContainer, StyledBorderBackDrop, StyledErrorAndMaxLengthContainer, };