import { useMemo, memo, forwardRef, PropsWithoutRef, RefAttributes } from 'react' import { TextProps as BaseTextProps, Text as BaseText, TextStyle } from 'react-native' import { DisplayVariant, displayVariants, fontDisplaySize, fontTextSize, FontWeight, fontWeights, TextVariant, textVariants, } from '../../config' import { generateStyledComponent, generateStyleSheet } from '../../utils' import { StyledProps } from '../types' import { useTheme } from '@/hooks' import { getColorValue, convertEmToNumber } from '@/utils' type ConditionalTextProps = | { type?: 'text' | never variant?: TextVariant } | { type: 'display' variant?: DisplayVariant } type TypographyProps = { fontSize?: TextStyle['fontSize'] letterSpacing?: LetterSpacings lineHeight?: LineHeights fontFamily?: Fonts fontWeight?: TextStyle['fontWeight'] color?: ColorNames noOfLines?: number textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify' textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase' textDecoration?: 'none' | 'underline' | 'line-through' | 'underline line-through' bold?: boolean italic?: boolean underline?: boolean strikeThrough?: boolean capitalize?: boolean uppercase?: boolean lowercase?: boolean } type TextProps = StyledProps & BaseTextProps & TypographyProps & ConditionalTextProps const RawText = memo( forwardRef( ( { bold, capitalize, color = 'text.primary', italic, letterSpacing, lineHeight, noOfLines, strikeThrough, textDecoration, textTransform, underline, uppercase, lowercase, style, type = 'text', variant = 'MdRegular', ...props }, ref ) => { const theme = useTheme() const { fontWeight: variantFontWeight, fontSize: variantFontSize } = useMemo(() => { if (type === 'text') { return textVariants[variant] ?? {} } return displayVariants[variant] ?? {} }, [type, variant]) const fontFamily = useMemo( (): FontWeight => props.fontFamily || variantFontWeight, [props.fontFamily, variantFontWeight] ) const fontWeight = useMemo( (): TextStyle['fontWeight'] => bold ? 'bold' : props.fontWeight || fontWeights[variantFontWeight], [bold, props.fontWeight, variantFontWeight] ) const fontSize = useMemo( (): number => props.fontSize || (type === 'text' ? fontTextSize[variantFontSize as keyof typeof fontTextSize] : fontDisplaySize[variantFontSize]), [props.fontSize, type, variantFontSize] ) const fontFamilyStyle = useMemo( () => ({ fontFamily: fontFamily ? theme.fonts[variantFontWeight] : undefined, }), [fontFamily, theme.fonts, variantFontWeight] ) const finalFontSize = useMemo( () => (fontSize && typeof fontSize === 'number' ? fontSize : undefined), [fontSize] ) const lineHeightStyle = useMemo( () => ({ lineHeight: lineHeight ? convertEmToNumber(theme.lineHeights[lineHeight], finalFontSize) : undefined, }), [theme, lineHeight, finalFontSize] ) const letterSpacingStyle = useMemo( () => ({ letterSpacing: letterSpacing ? convertEmToNumber(theme.letterSpacings[letterSpacing], finalFontSize) : undefined, }), [theme, letterSpacing, finalFontSize] ) const fontSizeStyle = useMemo( () => ({ fontSize: finalFontSize, }), [finalFontSize] ) const textColor = useMemo( () => ({ color: color ? getColorValue({ color, colors: theme.colors }) : theme.colors.text.primary, }), [theme, color] ) const textAlignmentStyle = useMemo( () => ({ textAlign: props.textAlign, }), [props.textAlign] ) const textTransformStyle = useMemo( () => ({ textTransform: capitalize || textTransform === 'capitalize' ? 'capitalize' : lowercase || textTransform === 'lowercase' ? 'lowercase' : uppercase || textTransform === 'uppercase' ? 'uppercase' : 'none', }), [capitalize, lowercase, uppercase, textTransform] ) const textDecorationStyle = useMemo( () => ({ textDecorationLine: underline || textDecoration === 'underline' ? 'underline' : strikeThrough || textDecoration === 'line-through' ? 'line-through' : undefined, }), [underline, strikeThrough, textDecoration] ) const textStyle = useMemo( () => generateStyleSheet([ { fontWeight }, italic && { fontStyle: 'italic' }, fontFamilyStyle, fontSizeStyle, letterSpacingStyle, lineHeightStyle, textAlignmentStyle, textColor, textDecorationStyle, textTransformStyle, style, ]), [ fontFamilyStyle, fontSizeStyle, fontWeight, italic, letterSpacingStyle, lineHeightStyle, style, textAlignmentStyle, textColor, textDecorationStyle, textTransformStyle, ] ) return } ) ) type TextComposition = React.ForwardRefExoticComponent< PropsWithoutRef & RefAttributes > & { [key in T]: React.ForwardRefExoticComponent & RefAttributes> } const Text = generateStyledComponent(RawText) as TextComposition const generateTextVariant = (variant: TextVariant) => forwardRef((props, ref) => ) Text.LgBold = generateTextVariant('LgBold') Text.LgBold.displayName = 'Text.LgBold' Text.LgMedium = generateTextVariant('LgMedium') Text.LgMedium.displayName = 'Text.LgMedium' Text.LgRegular = generateTextVariant('LgRegular') Text.LgRegular.displayName = 'Text.LgRegular' Text.LgSemibold = generateTextVariant('LgSemibold') Text.LgSemibold.displayName = 'Text.LgSemibold' Text.MdBold = generateTextVariant('MdBold') Text.MdBold.displayName = 'Text.MdBold' Text.MdMedium = generateTextVariant('MdMedium') Text.MdMedium.displayName = 'Text.MdMedium' Text.MdRegular = generateTextVariant('MdRegular') Text.MdRegular.displayName = 'Text.MdRegular' Text.MdSemibold = generateTextVariant('MdSemibold') Text.MdSemibold.displayName = 'Text.MdSemibold' Text.SmBold = generateTextVariant('SmBold') Text.SmBold.displayName = 'Text.SmBold' Text.SmMedium = generateTextVariant('SmMedium') Text.SmMedium.displayName = 'Text.SmMedium' Text.SmRegular = generateTextVariant('SmRegular') Text.SmRegular.displayName = 'Text.SmRegular' Text.SmSemibold = generateTextVariant('SmSemibold') Text.SmSemibold.displayName = 'Text.SmSemibold' Text.XsBold = generateTextVariant('XsBold') Text.XsBold.displayName = 'Text.XsBold' Text.XsMedium = generateTextVariant('XsMedium') Text.XsMedium.displayName = 'Text.XsMedium' Text.XsRegular = generateTextVariant('XsRegular') Text.XsRegular.displayName = 'Text.XsRegular' Text.XsSemibold = generateTextVariant('XsSemibold') Text.XsSemibold.displayName = 'Text.XsSemibold' Text.XlBold = generateTextVariant('XlBold') Text.XlBold.displayName = 'Text.XlBold' Text.XlMedium = generateTextVariant('XlMedium') Text.XlMedium.displayName = 'Text.XlMedium' Text.XlRegular = generateTextVariant('XlRegular') Text.XlRegular.displayName = 'Text.XlRegular' Text.XlSemibold = generateTextVariant('XlSemibold') Text.XlSemibold.displayName = 'Text.XlSemibold' Text.XxlBold = generateTextVariant('XxlBold') Text.XxlBold.displayName = 'Text.XxlBold' Text.XxlMedium = generateTextVariant('XxlMedium') Text.XxlMedium.displayName = 'Text.XxlMedium' Text.XxlRegular = generateTextVariant('XxlRegular') Text.XxlRegular.displayName = 'Text.XxlRegular' Text.XxlSemibold = generateTextVariant('XxlSemibold') Text.XxlSemibold.displayName = 'Text.XxlSemibold' export { Display, Text } const Display = generateStyledComponent(RawText) as TextComposition Display.displayName = 'Display' const generateDisplayVariant = (variant: DisplayVariant = 'MdRegular') => forwardRef((props, ref) => { return }) Display.LgMedium = generateDisplayVariant('LgMedium') Display.LgMedium.displayName = 'Display.LgMedium' Display.LgRegular = generateDisplayVariant('LgRegular') Display.LgRegular.displayName = 'Display.LgRegular' Display.LgSemibold = generateDisplayVariant('LgSemibold') Display.LgSemibold.displayName = 'Display.LgSemibold' Display.LgBold = generateDisplayVariant('LgBold') Display.LgBold.displayName = 'Display.LgBold' Display.MdBold = generateDisplayVariant('MdBold') Display.MdBold.displayName = 'Display.MdBold' Display.MdMedium = generateDisplayVariant('MdMedium') Display.MdMedium.displayName = 'Display.MdMedium' Display.MdRegular = generateDisplayVariant('MdRegular') Display.MdRegular.displayName = 'Display.MdRegular' Display.MdSemibold = generateDisplayVariant('MdSemibold') Display.MdSemibold.displayName = 'Display.MdSemibold' Display.SmBold = generateDisplayVariant('SmBold') Display.SmBold.displayName = 'Display.SmBold' Display.SmMedium = generateDisplayVariant('SmMedium') Display.SmMedium.displayName = 'Display.SmMedium' Display.SmRegular = generateDisplayVariant('SmRegular') Display.SmRegular.displayName = 'Display.SmRegular' Display.SmSemibold = generateDisplayVariant('SmSemibold') Display.SmSemibold.displayName = 'Display.SmSemibold' Display.XsBold = generateDisplayVariant('XsBold') Display.XsBold.displayName = 'Display.XsBold' Display.XsMedium = generateDisplayVariant('XsMedium') Display.XsMedium.displayName = 'Display.XsMedium' Display.XsRegular = generateDisplayVariant('XsRegular') Display.XsRegular.displayName = 'Display.XsRegular' Display.XsSemibold = generateDisplayVariant('XsSemibold') Display.XsSemibold.displayName = 'Display.XsSemibold' Display.XlBold = generateDisplayVariant('XlBold') Display.XlBold.displayName = 'Display.XlBold' Display.XlMedium = generateDisplayVariant('XlMedium') Display.XlMedium.displayName = 'Display.XlMedium' Display.XlRegular = generateDisplayVariant('XlRegular') Display.XlRegular.displayName = 'Display.XlRegular' Display.XlSemibold = generateDisplayVariant('XlSemibold') Display.XlSemibold.displayName = 'Display.XlSemibold' Display.XxlBold = generateDisplayVariant('XxlBold') Display.XxlBold.displayName = 'Display.XxlBold' Display.XxlMedium = generateDisplayVariant('XxlMedium') Display.XxlMedium.displayName = 'Display.XxlMedium' Display.XxlRegular = generateDisplayVariant('XxlRegular') Display.XxlRegular.displayName = 'Display.XxlRegular' Display.XxlSemibold = generateDisplayVariant('XxlSemibold') Display.XxlSemibold.displayName = 'Display.XxlSemibold'