diff --git a/src/components/TextInput/BaseTextInput/implementation/index.native.tsx b/src/components/TextInput/BaseTextInput/implementation/index.native.tsx index 1392077986e1e..77cfe5b62e09e 100644 --- a/src/components/TextInput/BaseTextInput/implementation/index.native.tsx +++ b/src/components/TextInput/BaseTextInput/implementation/index.native.tsx @@ -18,14 +18,12 @@ import type {BaseTextInputProps, BaseTextInputRef} from '@components/TextInput/B import * as styleConst from '@components/TextInput/styleConst'; import TextInputClearButton from '@components/TextInput/TextInputClearButton'; import TextInputLabel from '@components/TextInput/TextInputLabel'; -import TextInputMeasurement from '@components/TextInput/TextInputMeasurement'; import useHtmlPaste from '@hooks/useHtmlPaste'; import useLocalize from '@hooks/useLocalize'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import getPlatform from '@libs/getPlatform'; import isInputAutoFilled from '@libs/isInputAutoFilled'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -80,10 +78,6 @@ function BaseTextInput( }: BaseTextInputProps, ref: ForwardedRef, ) { - // For iOS, we don't need to measure the text input because it already has auto grow behavior - // See TextInputMeasurement.ios.tsx for more details - const isExternalAutoGrowMeasurement = getPlatform() !== CONST.PLATFORM.IOS && autoGrow; - const InputComponent = InputComponentMap.get(type) ?? RNTextInput; const isMarkdownEnabled = type === 'markdown'; const isAutoGrowHeightMarkdown = isMarkdownEnabled && autoGrowHeight; @@ -253,7 +247,7 @@ function BaseTextInput( styles.textInputContainer, textInputContainerStyles, !!contentWidth && StyleUtils.getWidthStyle(textInputWidth), - isExternalAutoGrowMeasurement && StyleUtils.getAutoGrowWidthInputContainerStyles(textInputWidth, autoGrowExtraSpace), + autoGrow && StyleUtils.getAutoGrowWidthInputContainerStyles(textInputWidth, autoGrowExtraSpace), !hideFocusedState && isFocused && styles.borderColorFocus, (!!hasError || !!errorText) && styles.borderColorDanger, autoGrowHeight && {scrollPaddingTop: typeof maxAutoGrowHeight === 'number' ? 2 * maxAutoGrowHeight : undefined}, @@ -265,10 +259,6 @@ function BaseTextInput( // Height fix is needed only for Text single line inputs const shouldApplyHeight = !isMultiline && !isMarkdownEnabled; - - // Fix iOS cursor jumping when entering first character using HW keyboard https://github.com/Expensify/App/pull/59078#issuecomment-2802834037 - const selection = inputProps.selection?.end === 0 && inputProps.selection?.start === 0 ? undefined : inputProps.selection; - return ( <> @@ -355,8 +345,8 @@ function BaseTextInput( placeholderTextColor={placeholderTextColor ?? theme.placeholderText} underlineColorAndroid="transparent" style={[ - !autoGrow && styles.flex1, - !autoGrow && styles.w100, + styles.flex1, + styles.w100, inputStyle, (!hasLabel || isMultiline) && styles.pv0, inputPaddingLeft, @@ -387,7 +377,7 @@ function BaseTextInput( keyboardType={inputProps.keyboardType} inputMode={!disableKeyboard ? inputProps.inputMode : CONST.INPUT_MODE.NONE} value={uncontrolled ? undefined : value} - selection={selection} + selection={inputProps.selection} readOnly={isReadOnly} defaultValue={defaultValue} markdownStyle={markdownStyle} @@ -451,21 +441,55 @@ function BaseTextInput( /> )} - + {!!contentWidth && isPrefixCharacterPaddingCalculated && ( + { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { + return; + } + setTextInputWidth(e.nativeEvent.layout.width); + setTextInputHeight(e.nativeEvent.layout.height); + }} + > + + {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} + {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} + + + )} + {/* + Text input component doesn't support auto grow by default. + This text view is used to calculate width or height of the input value given textStyle in this component. + This Text component is intentionally positioned out of the screen. + */} + {(!!autoGrow || autoGrowHeight) && !isAutoGrowHeightMarkdown && ( + { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { + return; + } + // Add +2 to width so that cursor is not cut off / covered at the end of text content + setTextInputWidth(e.nativeEvent.layout.width + 2); + setTextInputHeight(e.nativeEvent.layout.height); + }} + > + {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} + {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} + + )} ); } diff --git a/src/components/TextInput/BaseTextInput/implementation/index.tsx b/src/components/TextInput/BaseTextInput/implementation/index.tsx index 9d6e858b25515..0072c92f83745 100644 --- a/src/components/TextInput/BaseTextInput/implementation/index.tsx +++ b/src/components/TextInput/BaseTextInput/implementation/index.tsx @@ -19,14 +19,13 @@ import type {BaseTextInputProps, BaseTextInputRef} from '@components/TextInput/B import {ACTIVE_LABEL_SCALE, ACTIVE_LABEL_TRANSLATE_Y, INACTIVE_LABEL_SCALE, INACTIVE_LABEL_TRANSLATE_Y} from '@components/TextInput/styleConst'; import TextInputClearButton from '@components/TextInput/TextInputClearButton'; import TextInputLabel from '@components/TextInput/TextInputLabel'; -import TextInputMeasurement from '@components/TextInput/TextInputMeasurement'; import useHtmlPaste from '@hooks/useHtmlPaste'; import useLocalize from '@hooks/useLocalize'; import useMarkdownStyle from '@hooks/useMarkdownStyle'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {isMobileChrome} from '@libs/Browser'; +import {isMobileChrome, isMobileSafari, isSafari} from '@libs/Browser'; import {scrollToRight} from '@libs/InputUtils'; import isInputAutoFilled from '@libs/isInputAutoFilled'; import variables from '@styles/variables'; @@ -467,21 +466,63 @@ function BaseTextInput( /> )} - + {!!contentWidth && isPrefixCharacterPaddingCalculated && ( + { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { + return; + } + setTextInputWidth(e.nativeEvent.layout.width); + setTextInputHeight(e.nativeEvent.layout.height); + }} + > + + {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} + {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} + + + )} + {/* + Text input component doesn't support auto grow by default. + We're using a hidden text input to achieve that. + This text view is used to calculate width or height of the input value given textStyle in this component. + This Text component is intentionally positioned out of the screen. + */} + {(!!autoGrow || autoGrowHeight) && !isAutoGrowHeightMarkdown && ( + // Add +2 to width on Safari browsers so that text is not cut off due to the cursor or when changing the value + // Reference: https://github.com/Expensify/App/issues/8158, https://github.com/Expensify/App/issues/26628 + // For mobile Chrome, ensure proper display of the text selection handle (blue bubble down). + // Reference: https://github.com/Expensify/App/issues/34921 + { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { + return; + } + let additionalWidth = 0; + if (isMobileSafari() || isSafari() || isMobileChrome()) { + additionalWidth = 2; + } + setTextInputWidth(e.nativeEvent.layout.width + additionalWidth); + setTextInputHeight(e.nativeEvent.layout.height); + }} + > + {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} + {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} + + )} ); } diff --git a/src/components/TextInput/TextInputMeasurement/index.ios.tsx b/src/components/TextInput/TextInputMeasurement/index.ios.tsx deleted file mode 100644 index 2db4c300bd9bb..0000000000000 --- a/src/components/TextInput/TextInputMeasurement/index.ios.tsx +++ /dev/null @@ -1,7 +0,0 @@ -function TextInputMeasurement() { - return null; -} - -TextInputMeasurement.displayName = 'TextInputMeasurement'; - -export default TextInputMeasurement; diff --git a/src/components/TextInput/TextInputMeasurement/index.tsx b/src/components/TextInput/TextInputMeasurement/index.tsx deleted file mode 100644 index 0f0b982a9af60..0000000000000 --- a/src/components/TextInput/TextInputMeasurement/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React from 'react'; -import type {ViewStyle} from 'react-native'; -import {View} from 'react-native'; -import Text from '@components/Text'; -import useThemeStyles from '@hooks/useThemeStyles'; -import {isMobileChrome, isMobileSafari, isSafari} from '@libs/Browser'; -import type TextInputMeasurementProps from './types'; - -function TextInputMeasurement({ - value, - placeholder, - contentWidth, - autoGrowHeight, - maxAutoGrowHeight, - width, - inputStyle, - inputPaddingLeft, - autoGrow, - isAutoGrowHeightMarkdown, - onSetTextInputWidth, - onSetTextInputHeight, - isPrefixCharacterPaddingCalculated, -}: TextInputMeasurementProps) { - const styles = useThemeStyles(); - - return ( - <> - {!!contentWidth && isPrefixCharacterPaddingCalculated && ( - { - if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { - return; - } - onSetTextInputWidth(e.nativeEvent.layout.width); - onSetTextInputHeight(e.nativeEvent.layout.height); - }} - > - - {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} - {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} - - - )} - {/* - Text input component doesn't support auto grow by default. - We're using a hidden text input to achieve that. - This text view is used to calculate width or height of the input value given textStyle in this component. - This Text component is intentionally positioned out of the screen. - */} - {(!!autoGrow || !!autoGrowHeight) && !isAutoGrowHeightMarkdown && ( - // Add +2 to width on Safari browsers so that text is not cut off due to the cursor or when changing the value - // Reference: https://github.com/Expensify/App/issues/8158, https://github.com/Expensify/App/issues/26628 - // For mobile Chrome, ensure proper display of the text selection handle (blue bubble down). - // Reference: https://github.com/Expensify/App/issues/34921 - { - if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { - return; - } - let additionalWidth = 0; - if (isMobileSafari() || isSafari() || isMobileChrome()) { - additionalWidth = 2; - } - onSetTextInputWidth(e.nativeEvent.layout.width + additionalWidth); - onSetTextInputHeight(e.nativeEvent.layout.height); - }} - > - {/* \u200B added to solve the issue of not expanding the text input enough when the value ends with '\n' (https://github.com/Expensify/App/issues/21271) */} - {value ? `${value}${value.endsWith('\n') ? '\u200B' : ''}` : placeholder} - - )} - - ); -} - -TextInputMeasurement.displayName = 'TextInputMeasurement'; - -export default TextInputMeasurement; diff --git a/src/components/TextInput/TextInputMeasurement/types.ts b/src/components/TextInput/TextInputMeasurement/types.ts deleted file mode 100644 index 44625d067574b..0000000000000 --- a/src/components/TextInput/TextInputMeasurement/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; - -type TextInputMeasurementProps = { - /** The value to measure */ - value?: string; - - /** The placeholder to measure */ - placeholder?: string; - - /** The width to measure */ - contentWidth?: number; - - /** Whether to auto grow height */ - autoGrowHeight?: boolean; - - /** The maximum height for auto grow */ - maxAutoGrowHeight?: number; - - /** The width of the container */ - width: number | null; - - /** The input style */ - inputStyle?: StyleProp; - - /** The input padding left */ - inputPaddingLeft?: StyleProp; - - /** Whether to auto grow */ - autoGrow?: boolean; - - /** Whether the input is markdown */ - isAutoGrowHeightMarkdown?: boolean; - - /** Callback to set the text input width */ - onSetTextInputWidth: (width: number) => void; - - /** Callback to set the text input height */ - onSetTextInputHeight: (height: number) => void; - - /** Whether the prefix character padding is calculated */ - isPrefixCharacterPaddingCalculated: boolean; -}; - -export default TextInputMeasurementProps;