import { ReactNode, forwardRef, ForwardedRef } from "react" // eslint-disable-next-line no-restricted-imports import { StyleProp, Text as RNText, TextProps as RNTextProps, TextStyle } from "react-native" import { TOptions } from "i18next" import { useTranslation } from "react-i18next" import { isRTL, TxKeyPath } from "@/i18n" import { useAppTheme } from "@/theme/context" import type { ThemedStyle, ThemedStyleArray } from "@/theme/types" import { typography } from "@/theme/typography" import { fs } from "@/utils/responsive" type Sizes = keyof typeof $sizeStyles type Weights = keyof typeof typography.primary type Presets = "default" | "bold" | "heading" | "subheading" | "formLabel" | "formHelper" export interface TextProps extends RNTextProps { /** * Text which is looked up via i18n. */ tx?: TxKeyPath /** * The text to display if not using `tx` or nested components. */ text?: string /** * Optional options to pass to i18n. Useful for interpolation * as well as explicitly setting locale or translation fallbacks. */ txOptions?: TOptions /** * An optional style override useful for padding & margin. */ style?: StyleProp /** * One of the different types of text presets. */ preset?: Presets /** * Text weight modifier. */ weight?: Weights /** * Text size modifier. */ size?: Sizes /** * Children components. */ children?: ReactNode } /** * For your text displaying needs. * This component is a HOC over the built-in React Native one. * @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Text/} * @param {TextProps} props - The props for the `Text` component. * @returns {JSX.Element} The rendered `Text` component. */ export const Text = forwardRef(function Text(props: TextProps, ref: ForwardedRef) { const { weight, size, tx, txOptions, text, children, style: $styleOverride, ...rest } = props const { themed } = useAppTheme() const { t } = useTranslation() const i18nText = tx && t(tx, txOptions) const content = i18nText || text || children const preset: Presets = props.preset ?? "default" const $styles: StyleProp = [ $rtlStyle, themed($presets[preset]), weight && $fontWeightStyles[weight], size && $sizeStyles[size], $styleOverride, ] return ( {content} ) }) const $sizeStyles = { xxl: { fontSize: fs(36), lineHeight: fs(44) } satisfies TextStyle, xl: { fontSize: fs(24), lineHeight: fs(34) } satisfies TextStyle, lg: { fontSize: fs(20), lineHeight: fs(32) } satisfies TextStyle, md: { fontSize: fs(18), lineHeight: fs(26) } satisfies TextStyle, sm: { fontSize: fs(16), lineHeight: fs(24) } satisfies TextStyle, xs: { fontSize: fs(14), lineHeight: fs(21) } satisfies TextStyle, xxs: { fontSize: fs(12), lineHeight: fs(18) } satisfies TextStyle, } const $fontWeightStyles = Object.entries(typography.primary).reduce((acc, [weight, fontFamily]) => { return { ...acc, [weight]: { fontFamily } } }, {}) as Record const $baseStyle: ThemedStyle = (theme) => ({ ...$sizeStyles.sm, ...$fontWeightStyles.normal, color: theme.colors.text, }) const $presets: Record> = { default: [$baseStyle], bold: [$baseStyle, { ...$fontWeightStyles.bold }], heading: [ $baseStyle, { ...$sizeStyles.xxl, ...$fontWeightStyles.bold, }, ], subheading: [$baseStyle, { ...$sizeStyles.lg, ...$fontWeightStyles.medium }], formLabel: [$baseStyle, { ...$fontWeightStyles.medium }], formHelper: [$baseStyle, { ...$sizeStyles.sm, ...$fontWeightStyles.normal }], } const $rtlStyle: TextStyle = isRTL ? { writingDirection: "rtl" } : {}