119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
|
|
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<TextStyle>
|
||
|
|
/**
|
||
|
|
* 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<RNText>) {
|
||
|
|
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<TextStyle> = [
|
||
|
|
$rtlStyle,
|
||
|
|
themed($presets[preset]),
|
||
|
|
weight && $fontWeightStyles[weight],
|
||
|
|
size && $sizeStyles[size],
|
||
|
|
$styleOverride,
|
||
|
|
]
|
||
|
|
|
||
|
|
return (
|
||
|
|
<RNText {...rest} style={$styles} ref={ref}>
|
||
|
|
{content}
|
||
|
|
</RNText>
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
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<Weights, TextStyle>
|
||
|
|
|
||
|
|
const $baseStyle: ThemedStyle<TextStyle> = (theme) => ({
|
||
|
|
...$sizeStyles.sm,
|
||
|
|
...$fontWeightStyles.normal,
|
||
|
|
color: theme.colors.text,
|
||
|
|
})
|
||
|
|
|
||
|
|
const $presets: Record<Presets, ThemedStyleArray<TextStyle>> = {
|
||
|
|
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" } : {}
|