From d7e03afd5605046d125b4f424bd11195c8593058 Mon Sep 17 00:00:00 2001 From: Samuel Holmes Date: Thu, 11 Jan 2024 22:15:32 -0800 Subject: [PATCH] Fix placeholder and consistent Android/iOS styling for FlipInput2 We introduce a Placeholder component to ensure we have consistent rendering of placeholder text (no weird line-breaks on Android). Next we simplify the platform specific styling for Android with one negative marginRight prop measured on the input value's character length. --- src/components/themed/FlipInput2.tsx | 108 +++++++++++++++------------ 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/src/components/themed/FlipInput2.tsx b/src/components/themed/FlipInput2.tsx index d8a237f6659..e8406b401ed 100644 --- a/src/components/themed/FlipInput2.tsx +++ b/src/components/themed/FlipInput2.tsx @@ -136,22 +136,17 @@ export const FlipInput2 = React.forwardRef((props: Props, r const zeroAmount = zeroString(amounts[fieldNum]) const primaryAmount = zeroAmount && !amountFocused ? '' : amounts[fieldNum] - const placeholderOrAmount = zeroAmount && !amountFocused ? lstrings.string_tap_to_edit : primaryAmount - const showBottomCurrency = amountFocused || !zeroAmount + const isEnterTextMode = amountFocused || !zeroAmount const currencyName = fieldInfos[fieldNum].currencyName + const inputRef = inputRefs[fieldNum] return ( - - + inputRef.current?.focus()}> + ((props: Props, r onFocus={handleBottomFocus} onBlur={handleBottomBlur} /> - {showBottomCurrency ? {' ' + currencyName} : null} - - + {!isEnterTextMode ? {lstrings.string_tap_to_edit} : null} + {isEnterTextMode ? {' ' + currencyName} : null} + + ) } @@ -179,9 +175,9 @@ export const FlipInput2 = React.forwardRef((props: Props, r topText = `${topText} ${fieldInfo.currencyName}` return ( - + {topText} - + ) } @@ -275,7 +271,7 @@ const BackAnimatedView = styled(Animated.View)<{ animatedValue: SharedValue () => [ +const TopAmountText = styled(Text)(theme => () => [ { color: theme.textInputPlaceholderColor, fontFamily: theme.fontFaceDefault, @@ -284,31 +280,50 @@ const PlaceholderText = styled(Text)(theme => () => [ } ]) -const AmountAnimatedNumericInput = styledWithRef(AnimatedNumericInput)<{ focusAnimation: SharedValue }>(theme => ({ focusAnimation }) => { - const isIos = Platform.OS === 'ios' - const interpolateTextColor = useAnimatedColorInterpolateFn(theme.textInputTextColor, theme.textInputTextColorFocused) - return [ - { - paddingRight: isIos ? 0 : theme.rem(0.25), - includeFontPadding: false, - fontFamily: theme.fontFaceMedium, - fontSize: isIos ? theme.rem(1.5) : theme.rem(1.45) - }, - useAnimatedStyle(() => ({ - color: interpolateTextColor(focusAnimation) - })) - ] -}) +const AmountAnimatedNumericInput = styledWithRef(AnimatedNumericInput)<{ focusAnimation: SharedValue; value: string }>( + theme => + ({ focusAnimation, value }) => { + const isAndroid = Platform.OS === 'android' + const interpolateTextColor = useAnimatedColorInterpolateFn(theme.textInputTextColor, theme.textInputTextColorFocused) + const characterLength = value.length + return [ + { + includeFontPadding: false, + fontFamily: theme.fontFaceMedium, + fontSize: theme.rem(1.5), + // Android has more space added to the width of the input + // after the last character in the input. It seems to be + // setting a min-width to the input to roughly 2 characters in size. + // We can compensate for this with a negative margin when the character length + // is less then 2 characters. + marginRight: isAndroid ? -theme.rem(Math.max(0, 2 - characterLength) * 0.4) : 0, + padding: 0 + }, + useAnimatedStyle(() => ({ + color: interpolateTextColor(focusAnimation) + })) + ] + } +) + +const PlaceholderAnimatedText = styled(Animated.Text)(theme => ({ + // opacity: 0, + position: 'absolute', + left: 0, + top: 0, + includeFontPadding: false, + color: theme.textInputPlaceholderColor, + fontFamily: theme.fontFaceMedium, + fontSize: theme.rem(1.5) +})) + const CurrencySymbolAnimatedText = styled(Animated.Text)<{ focusAnimation: SharedValue }>(theme => ({ focusAnimation }) => { - const isIos = Platform.OS === 'ios' const interpolateTextColor = useAnimatedColorInterpolateFn(theme.textInputTextColor, theme.textInputTextColorFocused) return [ { - fontFamily: theme.fontFaceDefault, - fontSize: theme.rem(1), - includeFontPadding: false, - paddingTop: isIos ? theme.rem(0.125) : theme.rem(1), - marginLeft: isIos ? 0 : -theme.rem(0.25) + fontFamily: theme.fontFaceMedium, + fontSize: theme.rem(1.5), + includeFontPadding: false }, useAnimatedStyle(() => ({ color: interpolateTextColor(focusAnimation) @@ -316,23 +331,18 @@ const CurrencySymbolAnimatedText = styled(Animated.Text)<{ focusAnimation: Share ] }) -const BottomContainerView = styled(View)(theme => ({ - flexDirection: 'row', - marginRight: theme.rem(1.5), - minHeight: theme.rem(2) -})) - -const ValueContainerView = styled(View)(theme => { - const isIos = Platform.OS === 'ios' +const AmountFieldContainerTouchable = styled(TouchableWithoutFeedback)(theme => { return { - flexDirection: 'row', - marginRight: theme.rem(0.5), - marginLeft: isIos ? 0 : -6, - marginTop: isIos ? 0 : -theme.rem(0.75), - marginBottom: isIos ? 0 : -theme.rem(1) + marginRight: theme.rem(1.5), + minHeight: theme.rem(2) } }) +const BottomContainerView = styled(View)({ + flexDirection: 'row', + alignItems: 'center' +}) + function useAnimatedColorInterpolateFn(fromColor: string, toColor: string) { const interpolateFn = useMemo(() => { return (focusValue: SharedValue) => {