diff --git a/Libraries/Components/TextInput/TextInput.d.ts b/Libraries/Components/TextInput/TextInput.d.ts index 9cbcebaaac82..3ad310bd8ced 100644 --- a/Libraries/Components/TextInput/TextInput.d.ts +++ b/Libraries/Components/TextInput/TextInput.d.ts @@ -435,6 +435,11 @@ export interface TextInputAndroidProps { * When false, it will prevent the soft keyboard from showing when the field is focused. The default value is true */ showSoftInputOnFocus?: boolean | undefined; + + /** + * Vertically align text when `multiline` is set to true + */ + verticalAlign?: 'auto' | 'top' | 'bottom' | 'middle' | undefined; } /** diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 91b8e22f6d71..370e3053b10f 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -23,6 +23,7 @@ import StyleSheet, { type TextStyleProp, type ViewStyleProp, } from '../../StyleSheet/StyleSheet'; +import flattenStyle from '../../StyleSheet/flattenStyle'; import Text from '../../Text/Text'; import TextAncestor from '../../Text/TextAncestor'; import Platform from '../../Utilities/Platform'; @@ -1599,6 +1600,13 @@ const ExportedForwardRef: React.AbstractComponent< React.ElementRef> & ImperativeMethods, >, ) { + const style = flattenStyle(restProps.style); + + if (style?.verticalAlign != null) { + style.textAlignVertical = + verticalAlignToTextAlignVerticalMap[style.verticalAlign]; + } + return ( ); }); @@ -1659,5 +1668,12 @@ const styles = StyleSheet.create({ }, }); +const verticalAlignToTextAlignVerticalMap = { + auto: 'auto', + top: 'top', + bottom: 'bottom', + middle: 'center', +}; + // $FlowFixMe[unclear-type] Unclear type. Using `any` type is not safe. module.exports = ((ExportedForwardRef: any): TextInputType); diff --git a/Libraries/Components/View/ReactNativeStyleAttributes.js b/Libraries/Components/View/ReactNativeStyleAttributes.js index 1c1818fa5221..2511ad4755d8 100644 --- a/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -138,6 +138,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { textShadowRadius: true, textTransform: true, userSelect: true, + verticalAlign: true, writingDirection: true, /** diff --git a/Libraries/StyleSheet/StyleSheetTypes.d.ts b/Libraries/StyleSheet/StyleSheetTypes.d.ts index 1346e434f61b..1df286e2141c 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.d.ts +++ b/Libraries/StyleSheet/StyleSheetTypes.d.ts @@ -261,6 +261,7 @@ export interface TextStyleIOS extends ViewStyle { export interface TextStyleAndroid extends ViewStyle { textAlignVertical?: 'auto' | 'top' | 'bottom' | 'center' | undefined; + verticalAlign?: 'auto' | 'top' | 'bottom' | 'middle' | undefined; includeFontPadding?: boolean | undefined; } diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index 5ade22fdf0d2..5099d49f313d 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -671,6 +671,7 @@ export type ____TextStyle_InternalCore = $ReadOnly<{ textDecorationColor?: ____ColorValue_Internal, textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase', userSelect?: 'auto' | 'text' | 'none' | 'contain' | 'all', + verticalAlign?: 'auto' | 'top' | 'bottom' | 'middle', writingDirection?: 'auto' | 'ltr' | 'rtl', }>; diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index 10692df2e9aa..ac427bf8c736 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -178,6 +178,13 @@ const Text: React.AbstractComponent< _selectable = userSelectToSelectableMap[style.userSelect]; } + if (style?.verticalAlign != null) { + style = StyleSheet.compose(style, { + textAlignVertical: + verticalAlignToTextAlignVerticalMap[style.verticalAlign], + }); + } + if (__DEV__) { if (PressabilityDebug.isEnabled() && onPress != null) { style = StyleSheet.compose(restProps.style, { @@ -275,4 +282,11 @@ const userSelectToSelectableMap = { all: true, }; +const verticalAlignToTextAlignVerticalMap = { + auto: 'auto', + top: 'top', + bottom: 'bottom', + middle: 'center', +}; + module.exports = Text; diff --git a/packages/rn-tester/js/examples/Text/TextExample.android.js b/packages/rn-tester/js/examples/Text/TextExample.android.js index 6779684096e8..5f5b4a1f7f2d 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.android.js +++ b/packages/rn-tester/js/examples/Text/TextExample.android.js @@ -1001,4 +1001,26 @@ exports.examples = [ ); }, }, + { + title: 'Text alignment', + render: function (): React.Node { + return ( + + + Text element aligned to the top via textAlignVertical + + + Text element aligned to the top via verticalAlign + + + Text element aligned to the middle via textAlignVertical + + + Text element aligned to the middle via verticalAlign + + + ); + }, + }, ];