import * as React from 'react'; import { I18nManager, StyleProp, StyleSheet, Text as NativeText, TextStyle, } from 'react-native'; import AnimatedText from './AnimatedText'; import type { VariantProp } from './types'; import StyledText from './v2/StyledText'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; export type Props = React.ComponentProps & { /** * @supported Available in v5.x with theme version 3 * * Variant defines appropriate text styles for type role and its size. * Available variants: * * Display: `displayLarge`, `displayMedium`, `displaySmall` * * Headline: `headlineLarge`, `headlineMedium`, `headlineSmall` * * Title: `titleLarge`, `titleMedium`, `titleSmall` * * Label: `labelLarge`, `labelMedium`, `labelSmall` * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ variant?: VariantProp; children: React.ReactNode; theme?: ThemeProp; style?: StyleProp; }; export type TextRef = React.ForwardedRef<{ setNativeProps(args: Object): void; }>; // @component-group Typography /** * Typography component showing styles complied with passed `variant` prop and supported by the type system. * * ## Usage * ```js * import * as React from 'react'; * import { Text } from 'react-native-paper'; * * const MyComponent = () => ( * <> * Display Large * Display Medium * Display small * * Headline Large * Headline Medium * Headline Small * * Title Large * Title Medium * Title Small * * Body Large * Body Medium * Body Small * * Label Large * Label Medium * Label Small * * ); * * export default MyComponent; * ``` * * @extends Text props https://reactnative.dev/docs/text#props */ const Text = ( { style, variant, theme: initialTheme, ...rest }: Props, ref: TextRef ) => { const root = React.useRef(null); // FIXME: destructure it in TS 4.6+ const theme = useInternalTheme(initialTheme); const writingDirection = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'; React.useImperativeHandle(ref, () => ({ setNativeProps: (args: Object) => root.current?.setNativeProps(args), })); if (theme.isV3 && variant) { let font = theme.fonts[variant]; let textStyle = [font, style]; if ( React.isValidElement(rest.children) && (rest.children.type === Component || rest.children.type === AnimatedText || rest.children.type === StyledText) ) { const { props } = rest.children; // Context: Some components have the built-in `Text` component with a predefined variant, // that also accepts `children` as a `React.Node`. This can result in a situation, // where another `Text` component is rendered within the built-in `Text` component. // By doing that, we assume that user doesn't want to consume pre-defined font properties. // Case one: Nested `Text` has different `variant` that specified in parent. For example: // // Nested // // Solution: To address the following scenario, the code below overrides the `variant` // specified in a parent in favor of children's variant: if (props.variant) { font = theme.fonts[props.variant as VariantProp]; textStyle = [style, font]; } // Case two: Nested `Text` has specified `styles` which intefere // with font properties, from the parent's `variant`. For example: // // Nested // // Solution: To address the following scenario, the code below overrides the // parent's style with children's style: if (!props.variant) { textStyle = [style, props.style]; } } if (typeof font !== 'object') { throw new Error( `Variant ${variant} was not provided properly. Valid variants are ${Object.keys( theme.fonts ).join(', ')}.` ); } return ( ); } else { const font = theme.isV3 ? theme.fonts.default : theme.fonts?.regular; const textStyle = { ...font, color: theme.isV3 ? theme.colors?.onSurface : theme.colors.text, }; return ( ); } }; const styles = StyleSheet.create({ text: { textAlign: 'left', }, }); type TextComponent = ( props: Props & { ref?: React.RefObject } ) => JSX.Element; const Component = forwardRef(Text) as TextComponent; export const customText = () => Component as unknown as TextComponent; export default Component;