diff --git a/src/components/AmountForm.tsx b/src/components/AmountForm.tsx index f895c8e6b553..860af5a3d719 100644 --- a/src/components/AmountForm.tsx +++ b/src/components/AmountForm.tsx @@ -257,7 +257,6 @@ function AmountForm( textInput.current = ref; }} prefixCharacter={currency} - prefixContainerStyle={styles.pb2half} prefixStyle={styles.colorMuted} keyboardType={CONST.KEYBOARD_TYPE.DECIMAL_PAD} // On android autoCapitalize="words" is necessary when keyboardType="decimal-pad" or inputMode="decimal" to prevent input lag. diff --git a/src/components/AmountTextInput.tsx b/src/components/AmountTextInput.tsx index 723ce911b889..326c04e52b07 100644 --- a/src/components/AmountTextInput.tsx +++ b/src/components/AmountTextInput.tsx @@ -37,6 +37,9 @@ type AmountTextInputProps = { /** Style for the TextInput container */ containerStyle?: StyleProp; + /** Whether to apply padding to the input, some inputs doesn't require any padding, e.g. Amount input in money request flow */ + shouldApplyPaddingToContainer?: boolean; + /** Hide the focus styles on TextInput */ hideFocusedState?: boolean; } & Pick; @@ -54,6 +57,7 @@ function AmountTextInput( containerStyle, disableKeyboard = true, hideFocusedState = true, + shouldApplyPaddingToContainer = false, ...rest }: AmountTextInputProps, ref: ForwardedRef, @@ -85,6 +89,8 @@ function AmountTextInput( autoCorrect={false} spellCheck={false} disableKeyboardShortcuts + shouldUseFullInputHeight + shouldApplyPaddingToContainer={shouldApplyPaddingToContainer} // eslint-disable-next-line react/jsx-props-no-spreading {...rest} /> diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index e79863c7a527..9ac6e8e32937 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -16,18 +16,19 @@ import useSingleExecution from '@hooks/useSingleExecution'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import * as Browser from '@libs/Browser'; +import {isMobile} from '@libs/Browser'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; -import * as EmojiUtils from '@libs/EmojiUtils'; +import {getRemovedSkinToneEmoji} from '@libs/EmojiUtils'; +import type {EmojiPickerListItem} from '@libs/EmojiUtils'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; -import * as ReportUtils from '@libs/ReportUtils'; +import {shouldAutoFocusOnKeyPress} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import BaseEmojiPickerMenu from './BaseEmojiPickerMenu'; import type EmojiPickerMenuProps from './types'; import useEmojiPickerMenu from './useEmojiPickerMenu'; -const throttleTime = Browser.isMobile() ? 200 : 50; +const throttleTime = isMobile() ? 200 : 50; function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, ref: ForwardedRef) { const styles = useThemeStyles(); @@ -148,7 +149,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } // We allow typing in the search box if any key is pressed apart from Arrow keys. - if (searchInputRef.current && !isTextInputFocused(searchInputRef) && ReportUtils.shouldAutoFocusOnKeyPress(keyBoardEvent)) { + if (searchInputRef.current && !isTextInputFocused(searchInputRef) && shouldAutoFocusOnKeyPress(keyBoardEvent)) { searchInputRef.current.focus(); } }, @@ -247,7 +248,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r * so that the sticky headers function properly. * */ - const renderItem: ListRenderItem = useCallback( + const renderItem: ListRenderItem = useCallback( ({item, index, target}) => { const code = item.code; const types = 'types' in item ? item.types : undefined; @@ -267,8 +268,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r const emojiCode = typeof preferredSkinTone === 'number' && types?.at(preferredSkinTone) && preferredSkinTone !== -1 ? types.at(preferredSkinTone) : code; const isEmojiFocused = index === focusedIndex && isUsingKeyboardMovement; - const shouldEmojiBeHighlighted = - (index === focusedIndex && highlightEmoji) || (!!activeEmoji && EmojiUtils.getRemovedSkinToneEmoji(emojiCode) === EmojiUtils.getRemovedSkinToneEmoji(activeEmoji)); + const shouldEmojiBeHighlighted = (index === focusedIndex && highlightEmoji) || (!!activeEmoji && getRemovedSkinToneEmoji(emojiCode) === getRemovedSkinToneEmoji(activeEmoji)); const shouldFirstEmojiBeHighlighted = index === 0 && highlightFirstEmoji; return ( @@ -320,7 +320,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r arePointerEventsDisabled ? styles.pointerEventsNone : styles.pointerEventsAuto, ]} > - + {decomposeString(value, maxLength).at(index) ?? ''} diff --git a/src/components/MoneyRequestAmountInput.tsx b/src/components/MoneyRequestAmountInput.tsx index 13ac81468565..5361eefc49d9 100644 --- a/src/components/MoneyRequestAmountInput.tsx +++ b/src/components/MoneyRequestAmountInput.tsx @@ -3,7 +3,6 @@ import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} fr import type {NativeSyntheticEvent, StyleProp, TextStyle, ViewStyle} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import {useMouseContext} from '@hooks/useMouseContext'; -import useThemeStyles from '@hooks/useThemeStyles'; import {isMobileSafari} from '@libs/Browser'; import {convertToFrontendAmountAsString, getCurrencyDecimals} from '@libs/CurrencyUtils'; import getOperatingSystem from '@libs/getOperatingSystem'; @@ -92,6 +91,9 @@ type MoneyRequestAmountInputProps = { /** The width of inner content */ contentWidth?: number; + /** Whether to apply padding to the input, some inputs doesn't require any padding, e.g. Amount input in money request flow */ + shouldApplyPaddingToContainer?: boolean; + /** Whether the amount is negative */ isNegative?: boolean; @@ -103,6 +105,7 @@ type MoneyRequestAmountInputProps = { /** Whether to allow flipping amount */ allowFlippingAmount?: boolean; + /** The testID of the input. Used to locate this view in end-to-end tests. */ testID?: string; } & Pick; @@ -148,11 +151,11 @@ function MoneyRequestAmountInput( clearNegative, testID, submitBehavior, + shouldApplyPaddingToContainer = false, ...props }: MoneyRequestAmountInputProps, forwardedRef: ForwardedRef, ) { - const styles = useThemeStyles(); const {toLocaleDigit, numberFormat} = useLocalize(); const textInput = useRef(null); @@ -360,7 +363,7 @@ function MoneyRequestAmountInput( style={props.inputStyle} containerStyle={props.containerStyle} prefixStyle={props.prefixStyle} - prefixContainerStyle={[styles.pb2half, props.prefixContainerStyle]} + prefixContainerStyle={props.prefixContainerStyle} touchableInputWrapperStyle={props.touchableInputWrapperStyle} maxLength={maxLength} hideFocusedState={hideFocusedState} @@ -370,6 +373,7 @@ function MoneyRequestAmountInput( isNegative={isNegative} testID={testID} submitBehavior={submitBehavior} + shouldApplyPaddingToContainer={shouldApplyPaddingToContainer} /> ); } diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 29a7e491ab71..f5a5d248af23 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -667,14 +667,16 @@ function MoneyRequestConfirmationList({ hideFocusedState={false} hideCurrencySymbol formatAmountOnBlur - prefixContainerStyle={[styles.pv0]} - inputStyle={[styles.optionRowAmountInput]} - containerStyle={[styles.textInputContainer]} + prefixContainerStyle={[styles.pv0, styles.h100]} + prefixStyle={styles.lineHeightUndefined} + inputStyle={[styles.optionRowAmountInput, styles.lineHeightUndefined]} + containerStyle={[styles.textInputContainer, styles.pl2, styles.pr1]} touchableInputWrapperStyle={[styles.ml3]} onFormatAmount={convertToDisplayStringWithoutCurrency} onAmountChange={(value: string) => onSplitShareChange(participantOption.accountID ?? CONST.DEFAULT_NUMBER_ID, Number(value))} maxLength={formattedTotalAmount.length + 1} contentWidth={(formattedTotalAmount.length + 1) * 8} + shouldApplyPaddingToContainer /> ), })); @@ -688,8 +690,11 @@ function MoneyRequestConfirmationList({ selectedParticipants, styles.flexWrap, styles.pl2, + styles.pr1, + styles.h100, styles.textLabel, styles.pv0, + styles.lineHeightUndefined, styles.optionRowAmountInput, styles.textInputContainer, styles.ml3, diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 0aba3447bc88..2937cc830902 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -221,7 +221,7 @@ function SearchAutocompleteInput( onSubmitEditing={onSubmit} shouldUseDisabledStyles={false} textInputContainerStyles={[styles.borderNone, styles.pb0, styles.pl3]} - inputStyle={[inputWidth, {lineHeight: undefined}]} + inputStyle={[inputWidth, styles.lineHeightUndefined]} placeholderTextColor={theme.textSupporting} onFocus={() => { onFocus?.(); diff --git a/src/components/SelectionList/SplitListItem.tsx b/src/components/SelectionList/SplitListItem.tsx index c4accb5e7b4b..4d1516f090f7 100644 --- a/src/components/SelectionList/SplitListItem.tsx +++ b/src/components/SelectionList/SplitListItem.tsx @@ -135,12 +135,14 @@ function SplitListItem({ submitBehavior="blurAndSubmit" formatAmountOnBlur onAmountChange={onSplitExpenseAmountChange} - prefixContainerStyle={[styles.pv0]} - inputStyle={[styles.optionRowAmountInput]} - containerStyle={[styles.textInputContainer]} + prefixContainerStyle={[styles.pv0, styles.h100]} + prefixStyle={styles.lineHeightUndefined} + inputStyle={[styles.optionRowAmountInput, styles.lineHeightUndefined]} + containerStyle={[styles.textInputContainer, styles.pl2, styles.pr1]} touchableInputWrapperStyle={[styles.ml3]} maxLength={formattedOriginalAmount.length + 1} contentWidth={(formattedOriginalAmount.length + 1) * 8} + shouldApplyPaddingToContainer /> diff --git a/src/components/Text.tsx b/src/components/Text.tsx index 16aaf9a708c3..763f9ee3fb22 100644 --- a/src/components/Text.tsx +++ b/src/components/Text.tsx @@ -26,9 +26,15 @@ type TextProps = RNTextProps & /** The family of the font to use */ family?: keyof FontUtilsType['fontFamily']['platform']; + + /** Should apply default line height */ + shouldUseDefaultLineHeight?: boolean; }; -function Text({color, fontSize = variables.fontSizeNormal, textAlign = 'left', children, family = 'EXP_NEUE', style = {}, ...props}: TextProps, ref: ForwardedRef) { +function Text( + {color, fontSize = variables.fontSizeNormal, textAlign = 'left', children, family = 'EXP_NEUE', style = {}, shouldUseDefaultLineHeight = true, ...props}: TextProps, + ref: ForwardedRef, +) { const theme = useTheme(); const customStyle = useContext(CustomStylesForChildrenContext); @@ -41,7 +47,7 @@ function Text({color, fontSize = variables.fontSizeNormal, textAlign = 'left', c ...StyleSheet.flatten(customStyle), }; - if (!componentStyle.lineHeight && componentStyle.fontSize === variables.fontSizeNormal) { + if (!componentStyle.lineHeight && componentStyle.fontSize === variables.fontSizeNormal && shouldUseDefaultLineHeight) { componentStyle.lineHeight = variables.fontSizeNormalHeight; } diff --git a/src/components/TextInput/BaseTextInput/implementation/index.native.tsx b/src/components/TextInput/BaseTextInput/implementation/index.native.tsx index 98970ca4065c..4601c7fb4cf2 100644 --- a/src/components/TextInput/BaseTextInput/implementation/index.native.tsx +++ b/src/components/TextInput/BaseTextInput/implementation/index.native.tsx @@ -3,7 +3,7 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useRef, useState} from 'react'; import type {GestureResponderEvent, LayoutChangeEvent, NativeSyntheticEvent, StyleProp, TextInput, TextInputFocusEventData, ViewStyle} from 'react-native'; import {ActivityIndicator, StyleSheet, View} from 'react-native'; -import {useSharedValue, withSpring} from 'react-native-reanimated'; +import {Easing, useSharedValue, withTiming} from 'react-native-reanimated'; import Checkbox from '@components/Checkbox'; import FormHelpMessage from '@components/FormHelpMessage'; import Icon from '@components/Icon'; @@ -43,9 +43,11 @@ function BaseTextInput( iconLeft = null, icon = null, textInputContainerStyles, + shouldApplyPaddingToContainer = true, touchableInputWrapperStyle, containerStyles, inputStyle, + shouldUseFullInputHeight = false, forceActiveLabel = false, disableKeyboard = false, autoGrow = false, @@ -110,13 +112,24 @@ function BaseTextInput( const labelTranslateY = useSharedValue(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y); const input = useRef(null); const isLabelActive = useRef(initialActiveLabel); + const hasLabel = !!label?.length; useHtmlPaste(input, undefined, isMarkdownEnabled); const animateLabel = useCallback( (translateY: number, scale: number) => { - labelScale.set(withSpring(scale, {overshootClamping: false})); - labelTranslateY.set(withSpring(translateY, {overshootClamping: false})); + labelScale.set( + withTiming(scale, { + duration: 200, + easing: Easing.inOut(Easing.ease), + }), + ); + labelTranslateY.set( + withTiming(translateY, { + duration: 200, + easing: Easing.inOut(Easing.ease), + }), + ); }, [labelScale, labelTranslateY], ); @@ -177,9 +190,11 @@ function BaseTextInput( const heightToFitEmojis = 1; setWidth((prevWidth: number | null) => (autoGrowHeight ? layout.width : prevWidth)); - setHeight((prevHeight: number) => (!multiline ? layout.height + heightToFitEmojis : prevHeight)); + const borderWidth = styles.textInputContainer.borderWidth * 2; + const labelPadding = hasLabel ? styles.textInputContainer.padding : 0; + setHeight((prevHeight: number) => (!multiline ? layout.height + heightToFitEmojis - (labelPadding + borderWidth) : prevHeight)); }, - [autoGrowHeight, multiline], + [autoGrowHeight, multiline, styles.textInputContainer, hasLabel], ); // The ref is needed when the component is uncontrolled and we don't have a value prop @@ -239,7 +254,7 @@ function BaseTextInput( setPasswordHidden((prevPasswordHidden) => !prevPasswordHidden); }, []); - const hasLabel = !!label?.length; + const shouldAddPaddingBottom = isMultiline || (autoGrowHeight && !isAutoGrowHeightMarkdown && textInputHeight > variables.componentSizeLarge); const isReadOnly = inputProps.readOnly ?? inputProps.disabled; // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null, and errorText can be an empty string // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing @@ -247,20 +262,24 @@ function BaseTextInput( const placeholderValue = !!prefixCharacter || !!suffixCharacter || isFocused || !hasLabel || (hasLabel && forceActiveLabel) ? placeholder : undefined; const newTextInputContainerStyles: StyleProp = StyleSheet.flatten([ styles.textInputContainer, + !hasLabel && styles.pt0, textInputContainerStyles, - !!contentWidth && StyleUtils.getWidthStyle(textInputWidth), + !shouldApplyPaddingToContainer && styles.p0, + !!contentWidth && StyleUtils.getWidthStyle(textInputWidth + (shouldApplyPaddingToContainer ? styles.textInputContainer.padding * 2 : 0)), autoGrow && StyleUtils.getAutoGrowWidthInputContainerStyles(textInputWidth, autoGrowExtraSpace, autoGrowMarginSide), !hideFocusedState && isFocused && styles.borderColorFocus, (!!hasError || !!errorText) && styles.borderColorDanger, autoGrowHeight && {scrollPaddingTop: typeof maxAutoGrowHeight === 'number' ? 2 * maxAutoGrowHeight : undefined}, isAutoGrowHeightMarkdown && styles.pb2, + inputProps.disabled && styles.textInputDisabledContainer, + shouldAddPaddingBottom && styles.pb1, ]); const inputPaddingLeft = !!prefixCharacter && StyleUtils.getPaddingLeft(prefixCharacterPadding + styles.pl1.paddingLeft); const inputPaddingRight = !!suffixCharacter && StyleUtils.getPaddingRight(StyleUtils.getCharacterPadding(suffixCharacter) + styles.pr1.paddingRight); // Height fix is needed only for Text single line inputs - const shouldApplyHeight = !isMultiline && !isMarkdownEnabled; + const shouldApplyHeight = !shouldUseFullInputHeight && !isMultiline && !isMarkdownEnabled; return ( <> @@ -276,7 +295,11 @@ function BaseTextInput( style={[ autoGrowHeight && !isAutoGrowHeightMarkdown && - styles.autoGrowHeightInputContainer(textInputHeight, variables.componentSizeLarge, typeof maxAutoGrowHeight === 'number' ? maxAutoGrowHeight : 0), + styles.autoGrowHeightInputContainer( + textInputHeight + (shouldAddPaddingBottom ? styles.textInputContainer.padding : 0), + variables.componentSizeLarge, + typeof maxAutoGrowHeight === 'number' ? maxAutoGrowHeight : 0, + ), isAutoGrowHeightMarkdown && {minHeight: variables.componentSizeLarge}, !isMultiline && styles.componentHeightLarge, touchableInputWrapperStyle, @@ -294,7 +317,7 @@ function BaseTextInput( <> {/* Adding this background to the label only for multiline text input, to prevent text overlapping with label when scrolling */} - {isMultiline && } + {isMultiline && } { setValue(''); onClearInput?.(); @@ -418,12 +441,18 @@ function BaseTextInput( )} {!!inputProps.secureTextEntry && ( { event.preventDefault(); @@ -437,7 +466,7 @@ function BaseTextInput( )} {!inputProps.secureTextEntry && !!icon && ( - + , undefined, isMarkdownEnabled); + useHtmlPaste(input as RefObject, undefined, isMarkdownEnabled); // AutoFocus which only works on mount: useEffect(() => { @@ -130,8 +131,18 @@ function BaseTextInput( const animateLabel = useCallback( (translateY: number, scale: number) => { - labelScale.set(withSpring(scale, {overshootClamping: false})); - labelTranslateY.set(withSpring(translateY, {overshootClamping: false})); + labelScale.set( + withTiming(scale, { + duration: 200, + easing: Easing.inOut(Easing.ease), + }), + ); + labelTranslateY.set( + withTiming(translateY, { + duration: 200, + easing: Easing.inOut(Easing.ease), + }), + ); }, [labelScale, labelTranslateY], ); @@ -246,7 +257,9 @@ function BaseTextInput( const togglePasswordVisibility = useCallback(() => { setPasswordHidden((prevPasswordHidden: boolean | undefined) => !prevPasswordHidden); }, []); + const isMultiline = multiline || autoGrowHeight; + const shouldAddPaddingBottom = isMultiline || (autoGrowHeight && !isAutoGrowHeightMarkdown && textInputHeight > variables.componentSizeLarge); const hasLabel = !!label?.length; const isReadOnly = inputProps.readOnly ?? inputProps.disabled; // Disabling this line for safeness as nullish coalescing works only if the value is undefined or null, and errorText can be an empty string @@ -255,14 +268,17 @@ function BaseTextInput( const newPlaceholder = !!prefixCharacter || !!suffixCharacter || isFocused || !hasLabel || (hasLabel && forceActiveLabel) ? placeholder : undefined; const newTextInputContainerStyles: StyleProp = StyleSheet.flatten([ styles.textInputContainer, + !shouldApplyPaddingToContainer && styles.p0, + !hasLabel && styles.pt0, textInputContainerStyles, - (autoGrow || !!contentWidth) && StyleUtils.getWidthStyle(textInputWidth), + (autoGrow || !!contentWidth) && StyleUtils.getWidthStyle(textInputWidth + (shouldApplyPaddingToContainer ? styles.textInputContainer.padding * 2 : 0)), !hideFocusedState && isFocused && styles.borderColorFocus, (!!hasError || !!errorText) && styles.borderColorDanger, autoGrowHeight && {scrollPaddingTop: typeof maxAutoGrowHeight === 'number' ? 2 * maxAutoGrowHeight : undefined}, isAutoGrowHeightMarkdown && styles.pb2, + inputProps.disabled && shouldUseDisabledStyles && styles.textInputDisabledContainer, + shouldAddPaddingBottom && styles.pb1, ]); - const isMultiline = multiline || autoGrowHeight; const inputPaddingLeft = !!prefixCharacter && StyleUtils.getPaddingLeft(prefixCharacterPadding + styles.pl1.paddingLeft); const inputPaddingRight = !!suffixCharacter && StyleUtils.getPaddingRight(StyleUtils.getCharacterPadding(suffixCharacter) + styles.pr1.paddingRight); @@ -288,7 +304,11 @@ function BaseTextInput( style={[ autoGrowHeight && !isAutoGrowHeightMarkdown && - styles.autoGrowHeightInputContainer(textInputHeight, variables.componentSizeLarge, typeof maxAutoGrowHeight === 'number' ? maxAutoGrowHeight : 0), + styles.autoGrowHeightInputContainer( + textInputHeight + (shouldAddPaddingBottom ? styles.textInputContainer.padding : 0), + variables.componentSizeLarge, + typeof maxAutoGrowHeight === 'number' ? maxAutoGrowHeight : 0, + ), isAutoGrowHeightMarkdown && {minHeight: variables.componentSizeLarge}, !isMultiline && styles.componentHeightLarge, touchableInputWrapperStyle, @@ -306,7 +326,15 @@ function BaseTextInput( <> {/* Adding this background to the label only for multiline text input, to prevent text overlapping with label when scrolling */} - {isMultiline && } + {isMultiline && ( + + )} {prefixCharacter} @@ -423,7 +452,7 @@ function BaseTextInput( }} > { setValue(''); onClearInput?.(); @@ -435,12 +464,18 @@ function BaseTextInput( )} {!!inputProps.secureTextEntry && ( { e.preventDefault(); @@ -454,7 +489,7 @@ function BaseTextInput( )} {!inputProps.secureTextEntry && !!icon && ( - + ; + /** Whether to apply padding to the input, some inputs doesn't require any padding, e.g. Amount input in money request flow */ + shouldApplyPaddingToContainer?: boolean; + /** Customizes the touchable wrapper of the TextInput component */ touchableInputWrapperStyle?: StyleProp; @@ -177,6 +180,9 @@ type CustomBaseTextInputProps = { /** Callback when the input is cleared using the clear button */ onClearInput?: () => void; + + /** Whether the input should be enforced to take full height of container. Default is `false` */ + shouldUseFullInputHeight?: boolean; }; type BaseTextInputRef = HTMLFormElement | AnimatedTextInputRef; diff --git a/src/components/TextInput/TextInputLabel/index.native.tsx b/src/components/TextInput/TextInputLabel/index.native.tsx index f332ba6ffa4d..c3fa2b876e12 100644 --- a/src/components/TextInput/TextInputLabel/index.native.tsx +++ b/src/components/TextInput/TextInputLabel/index.native.tsx @@ -7,14 +7,17 @@ function TextInputLabel({label, labelScale, labelTranslateY}: TextInputLabelProp const styles = useThemeStyles(); const animatedStyle = useAnimatedStyle(() => styles.textInputLabelTransformation(labelTranslateY, labelScale)); + const animatedStyleForText = useAnimatedStyle(() => styles.textInputLabelTransformation(labelTranslateY, labelScale, true)); return ( - - {label} - + + + {label} + + ); } diff --git a/src/components/TextInput/TextInputLabel/index.tsx b/src/components/TextInput/TextInputLabel/index.tsx index 110193bbc653..dd645476526e 100644 --- a/src/components/TextInput/TextInputLabel/index.tsx +++ b/src/components/TextInput/TextInputLabel/index.tsx @@ -26,7 +26,7 @@ function TextInputLabel({for: inputId = '', label, labelTranslateY, labelScale}: // eslint-disable-next-line react-compiler/react-compiler ref={textRef(labelRef)} role={CONST.ROLE.PRESENTATION} - style={[styles.textInputLabel, animatedStyle, styles.pointerEventsNone]} + style={[styles.textInputLabelContainer, styles.textInputLabel, animatedStyle, styles.pointerEventsNone]} > {label} diff --git a/src/components/TextInput/index.native.tsx b/src/components/TextInput/index.native.tsx index acc40295d575..2da80b13859c 100644 --- a/src/components/TextInput/index.native.tsx +++ b/src/components/TextInput/index.native.tsx @@ -35,6 +35,7 @@ function TextInput(props: BaseTextInputProps, ref: ForwardedRef ); } diff --git a/src/components/TextInput/styleConst.ts b/src/components/TextInput/styleConst.ts index 7d2f3021e3be..f6c3fc9b48b3 100644 --- a/src/components/TextInput/styleConst.ts +++ b/src/components/TextInput/styleConst.ts @@ -1,6 +1,6 @@ import variables from '@styles/variables'; -const ACTIVE_LABEL_TRANSLATE_Y = 4; +const ACTIVE_LABEL_TRANSLATE_Y = 7; const ACTIVE_LABEL_SCALE = 0.8668; const INACTIVE_LABEL_TRANSLATE_Y = variables.INACTIVE_LABEL_TRANSLATE_Y; diff --git a/src/components/TextInputWithCurrencySymbol/types.ts b/src/components/TextInputWithCurrencySymbol/types.ts index 7d5607a617fe..614432366433 100644 --- a/src/components/TextInputWithCurrencySymbol/types.ts +++ b/src/components/TextInputWithCurrencySymbol/types.ts @@ -78,6 +78,9 @@ type BaseTextInputWithCurrencySymbolProps = { /** Hide the focus styles on TextInput */ hideFocusedState?: boolean; + /** Whether to apply padding to the input, some inputs doesn't require any padding, e.g. Amount input in money request flow */ + shouldApplyPaddingToContainer?: boolean; + /** Whether the amount is negative */ isNegative?: boolean; diff --git a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx index 56fef5628858..7e7c8853dce0 100644 --- a/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx +++ b/src/pages/Debug/ReportAction/DebugReportActionCreatePage.tsx @@ -99,7 +99,8 @@ function DebugReportActionCreatePage({ multiline value={draftReportAction} onChangeText={editJSON} - textInputContainerStyles={[styles.border, styles.borderBottom, styles.p5]} + // We need to explicitly add styles.pt5 and styles.pb5 to override the default top and bottom padding of the text input + textInputContainerStyles={[styles.border, styles.borderBottom, styles.ph5, styles.pt5, styles.pb5]} /> diff --git a/src/pages/Debug/TransactionViolation/DebugTransactionViolationCreatePage.tsx b/src/pages/Debug/TransactionViolation/DebugTransactionViolationCreatePage.tsx index 93c6d26597dc..3d3ac18a2103 100644 --- a/src/pages/Debug/TransactionViolation/DebugTransactionViolationCreatePage.tsx +++ b/src/pages/Debug/TransactionViolation/DebugTransactionViolationCreatePage.tsx @@ -115,7 +115,8 @@ function DebugTransactionViolationCreatePage({ multiline value={draftTransactionViolation} onChangeText={editJSON} - textInputContainerStyles={[styles.border, styles.borderBottom, styles.p5]} + // We need to explicitly add styles.pt5 and styles.pb5 to override the default top and bottom padding of the text input + textInputContainerStyles={[styles.border, styles.borderBottom, styles.ph5, styles.pt5, styles.pb5]} /> {translate('debug.hint')} diff --git a/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx b/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx index 076b77f67993..e773baa5582f 100644 --- a/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx +++ b/src/pages/OnboardingPrivateDomain/BaseOnboardingPrivateDomain.tsx @@ -77,7 +77,7 @@ function BaseOnboardingPrivateDomain({shouldUseNativeStyles, route}: BaseOnboard > {translate('onboarding.peopleYouMayKnow')} - {translate('onboarding.workspaceYouMayJoin', {domain, email})} + {translate('onboarding.workspaceYouMayJoin', {domain, email})} { diff --git a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx index fd2a0f53e0bb..cc79e63da719 100644 --- a/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx +++ b/src/pages/ReimbursementAccount/USD/BusinessInfo/subSteps/IncorporationCode.tsx @@ -19,7 +19,7 @@ const STEP_FIELDS = [COMPANY_INCORPORATION_CODE_KEY]; function IncorporationCode({onNext, isEditing}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT); + const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: false}); const handleSubmit = useReimbursementAccountStepFormSubmit({ fieldIds: STEP_FIELDS, @@ -49,7 +49,7 @@ function IncorporationCode({onNext, isEditing}: SubStepProps) { submitButtonStyles={[styles.ph5, styles.mb0]} shouldHideFixErrorsAlert > - {translate('companyStep.industryClassification')} + {translate('companyStep.industryClassification')} ): FormInputErrors => { @@ -63,12 +63,11 @@ function IncorporationDateBusiness({onNext, isEditing}: SubStepProps) { submitButtonStyles={[styles.mb0]} shouldHideFixErrorsAlert > - {translate('businessInfoStep.selectYourCompanyIncorporationDate')} + {translate('businessInfoStep.selectYourCompanyIncorporationDate')} - {translate(`workspace.netsuite.tokenInput.formSteps.enterCredentials.title`)} + {translate(`workspace.netsuite.tokenInput.formSteps.enterCredentials.title`)} verticalAlignTop: { verticalAlign: 'top', }, + + lineHeightUndefined: { + lineHeight: undefined, + }, + lineHeightLarge: { lineHeight: variables.lineHeightLarge, }, + lineHeightXLarge: { lineHeight: variables.lineHeightXLarge, }, + label: { fontSize: variables.fontSizeLabel, lineHeight: variables.lineHeightLarge, @@ -1087,11 +1094,13 @@ const styles = (theme: ThemeColors) => borderColor: theme.danger, }, - textInputDisabled: { + textInputDisabledContainer: { // Adding disabled color theme to indicate user that the field is not editable. backgroundColor: theme.highlightBG, - borderBottomWidth: 2, borderColor: theme.borderLighter, + }, + + textInputDisabled: { // Adding browser specific style to bring consistency between Safari and other platforms. // Applying the Webkit styles only to browsers as it is not available in native. ...(getBrowser() @@ -1293,7 +1302,10 @@ const styles = (theme: ThemeColors) => height: '100%', backgroundColor: 'transparent', overflow: 'hidden', - borderBottomWidth: 2, + borderWidth: 1, + padding: 8, + paddingBottom: 0, + borderRadius: 8, borderColor: theme.border, }, @@ -1307,18 +1319,22 @@ const styles = (theme: ThemeColors) => textAlign: 'right', }, - textInputLabel: { + textInputLabelContainer: { position: 'absolute', - left: 0, + left: 8, + paddingRight: 16, top: 0, - fontSize: variables.fontSizeNormal, - color: theme.textSupporting, - ...FontUtils.fontFamily.platform.EXP_NEUE, width: '100%', zIndex: 1, transformOrigin: 'left center', }, + textInputLabel: { + fontSize: variables.fontSizeNormal, + color: theme.textSupporting, + ...FontUtils.fontFamily.platform.EXP_NEUE, + }, + textInputLabelBackground: { position: 'absolute', top: 0, @@ -1327,9 +1343,15 @@ const styles = (theme: ThemeColors) => backgroundColor: theme.componentBG, }, - textInputLabelTransformation: (translateY: SharedValue, scale: SharedValue) => { + textInputLabelTransformation: (translateY: SharedValue, scale: SharedValue, isForTextComponent?: boolean) => { 'worklet'; + if (isForTextComponent) { + return { + fontSize: interpolate(scale.get(), [0, ACTIVE_LABEL_SCALE], [0, variables.fontSizeLabel]), + } satisfies TextStyle; + } + return { transform: [{translateY: translateY.get()}], fontSize: interpolate(scale.get(), [0, ACTIVE_LABEL_SCALE], [0, variables.fontSizeLabel]), @@ -1341,7 +1363,7 @@ const styles = (theme: ThemeColors) => fontSize: variables.fontSizeNormal, lineHeight: variables.lineHeightXLarge, color: theme.text, - paddingTop: 23, + paddingTop: 15, paddingBottom: 8, paddingLeft: 0, borderWidth: 0, @@ -1353,7 +1375,7 @@ const styles = (theme: ThemeColors) => textInputMultilineContainer: { height: '100%', - paddingTop: 23, + paddingTop: 15, }, textInputAndIconContainer: (isMarkdownEnabled: boolean) => { @@ -1369,11 +1391,6 @@ const styles = (theme: ThemeColors) => textInputDesktop: addOutlineWidth(theme, {}, 0), - textInputIconContainer: { - paddingHorizontal: 11, - justifyContent: 'center', - }, - textInputLeftIconContainer: { justifyContent: 'center', paddingRight: 8, @@ -1404,23 +1421,22 @@ const styles = (theme: ThemeColors) => position: 'absolute', left: 0, top: 0, - height: variables.inputHeight, display: 'flex', flexDirection: 'row', alignItems: 'center', - paddingTop: 23, + paddingTop: 15, paddingBottom: 8, + height: '100%', }, textInputSuffixWrapper: { position: 'absolute', right: 0, top: 0, - height: variables.inputHeight, display: 'flex', flexDirection: 'row', alignItems: 'center', - paddingTop: 23, + paddingTop: 15, paddingBottom: 8, }, @@ -3331,7 +3347,7 @@ const styles = (theme: ThemeColors) => magicCodeInputContainer: { flexDirection: 'row', justifyContent: 'space-between', - minHeight: variables.inputHeight, + height: variables.inputHeight, }, magicCodeInput: { @@ -3818,15 +3834,13 @@ const styles = (theme: ThemeColors) => }, searchAutocompleteInputResults: { - backgroundColor: theme.sidebarHover, borderWidth: 1, - borderColor: theme.sidebarHover, + borderColor: theme.border, }, searchAutocompleteInputResultsFocused: { borderWidth: 1, borderColor: theme.success, - backgroundColor: theme.appBG, }, searchTableHeaderActive: { diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 5cf3a0c4bbc8..b053f0b37561 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1329,7 +1329,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ ...styles.overflowHidden, // maxHeight is not of the input only but the of the whole input container // which also includes the top padding and bottom border - height: maxHeight - styles.textInputMultilineContainer.paddingTop - styles.textInputContainer.borderBottomWidth, + height: maxHeight - styles.textInputMultilineContainer.paddingTop - styles.textInputContainer.borderWidth * 2, }; }, @@ -1350,7 +1350,19 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ getMarkdownMaxHeight: (maxAutoGrowHeight: number | undefined): TextStyle => { // maxHeight is not of the input only but the of the whole input container // which also includes the top padding and bottom border - return maxAutoGrowHeight ? {maxHeight: maxAutoGrowHeight - styles.textInputMultilineContainer.paddingTop - styles.textInputContainer.borderBottomWidth} : {}; + return maxAutoGrowHeight ? {maxHeight: maxAutoGrowHeight - styles.textInputMultilineContainer.paddingTop - styles.textInputContainer.borderWidth * 2} : {}; + }, + + /** + * Computes styles for the text input icon container. + * Applies horizontal padding if requested, and sets the top margin based on the presence of a label. + */ + getTextInputIconContainerStyles: (hasLabel: boolean, includePadding = true) => { + const paddingStyle = includePadding ? {paddingHorizontal: 11} : {}; + return { + ...paddingStyle, + marginTop: hasLabel ? 8 : 16, + }; }, /** @@ -1534,7 +1546,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ /** * Return the height of magic code input container */ - getHeightOfMagicCodeInput: (): ViewStyle => ({height: styles.magicCodeInputContainer.minHeight - styles.textInputContainer.borderBottomWidth}), + getHeightOfMagicCodeInput: (): ViewStyle => ({height: styles.magicCodeInputContainer.height - styles.textInputContainer.borderWidth * 2}), /** * Generate fill color of an icon based on its state. diff --git a/src/styles/variables.ts b/src/styles/variables.ts index b79a3435b8b5..9fce8c472a60 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -125,7 +125,7 @@ export default { formErrorLineHeight: getValueUsingPixelRatio(18, 23), communicationsLinkHeight: getValueUsingPixelRatio(20, 30), alternateTextHeight: getValueUsingPixelRatio(20, 24), - INACTIVE_LABEL_TRANSLATE_Y: getValueUsingPixelRatio(16, 21), + INACTIVE_LABEL_TRANSLATE_Y: getValueUsingPixelRatio(17, 22), sliderBarHeight: 8, sliderKnobSize: 26, checkboxLabelActiveOpacity: 0.7,