import { useRef, useEffect, ReactNode, useMemo, useState, useCallback, memo } from 'react'; import { Animated } from 'react-native'; import type { LayoutChangeEvent, StyleProp, TextStyle, TextProps } from 'react-native'; import { StyleSheet } from 'react-native-unistyles'; import { getRegisteredMoleculesComponentStyles, registerComponentsStyles } from '../../core'; const helperTextStylesDefault = StyleSheet.create(theme => ({ root: { fontSize: theme.typescale.bodySmall.fontSize, paddingVertical: theme.spacings['1'], paddingHorizontal: theme.spacings['4'], variants: { variant: { error: { color: theme.colors.error, }, info: { color: theme.colors.onSurfaceVariant, }, }, }, }, })); registerComponentsStyles({ HelperText: helperTextStylesDefault, }); export const styles = getRegisteredMoleculesComponentStyles('HelperText'); export type Props = TextProps & { /** * Type of the helper text. */ variant?: 'error' | 'info'; /** * Whether to display the helper text. */ visible?: boolean; /** * Text content of the HelperText. */ children: ReactNode; style?: StyleProp; /** * TestID used for testing purposes */ testID?: string; }; /** * Helper text is used in conjuction with input elements to provide additional hints for the user. * *
* *
* * ## Usage * ```js * import * as React from 'react'; * import { View } from 'react-native'; * import { HelperText, TextInput } from 'react-native-paper'; * * const MyComponent = () => { * const [text, setText] = React.useState(''); * * const onChangeText = text => setText(text); * * const hasErrors = () => { * return !text.includes('@'); * }; * * return ( * * * * Email address is invalid! * * * ); * }; * * export default MyComponent; * ``` */ const HelperText = ({ style: styleProp, variant = 'info', visible = true, onLayout, maxFontSizeMultiplier = 1.5, ...rest }: Props) => { styles.useVariants({ variant, }); const { current: shown } = useRef(new Animated.Value(visible ? 1 : 0)); const [textHeight, setTextHeight] = useState(0); const { textStyle } = useMemo(() => { // @ts-ignore return { textStyle: [ styles.root, { opacity: shown, transform: visible && variant === 'error' ? [ { translateY: shown.interpolate({ inputRange: [0, 1], outputRange: [-textHeight / 2, 0], }), }, ] : [], }, styleProp, ], }; }, [shown, styleProp, textHeight, variant, visible]); useEffect(() => { if (visible) { // show text Animated.timing(shown, { toValue: 1, duration: 150, useNativeDriver: true, }).start(); } else { // hide text Animated.timing(shown, { toValue: 0, duration: 180, useNativeDriver: true, }).start(); } }, [visible, shown]); const handleTextLayout = useCallback( (e: LayoutChangeEvent) => { onLayout?.(e); setTextHeight(e.nativeEvent.layout.height); }, [onLayout], ); return ( {rest.children} ); }; export default memo(HelperText);