diff options
author | Paul Frazee <pfrazee@gmail.com> | 2024-11-23 16:20:24 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-23 16:20:24 -0800 |
commit | 32bf8122e8c8a0fbadd53b8a015cfbc9014519a2 (patch) | |
tree | 55bd24596e6fadadbf4326b26e3d14e418c5c7bb /src/components/Typography.tsx | |
parent | 523d1f01a51c0e85e49916fb42b204f7004ffac1 (diff) | |
parent | b4d07c4112b9a62b5380948051aa4a7fd391a2d4 (diff) | |
download | voidsky-32bf8122e8c8a0fbadd53b8a015cfbc9014519a2.tar.zst |
Merge branch 'main' into main
Diffstat (limited to 'src/components/Typography.tsx')
-rw-r--r-- | src/components/Typography.tsx | 149 |
1 files changed, 10 insertions, 139 deletions
diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 69e073271..3e202cb8f 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,140 +1,15 @@ -import React from 'react' -import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' -import createEmojiRegex from 'emoji-regex' import {logger} from '#/logger' -import {isIOS, isNative} from '#/platform/detection' -import {Alf, applyFonts, atoms, flatten, useAlf, useTheme, web} from '#/alf' +import {atoms, flatten, useAlf, useTheme, web} from '#/alf' +import { + childHasEmoji, + normalizeTextStyles, + renderChildrenWithEmoji, + TextProps, +} from '#/alf/typography' import {IS_DEV} from '#/env' - -export type StringChild = string | (string | null)[] - -export type TextProps = Omit<RNTextProps, 'children'> & { - /** - * Lets the user select text, to use the native copy and paste functionality. - */ - selectable?: boolean - /** - * Provides `data-*` attributes to the underlying `UITextView` component on - * web only. - */ - dataSet?: Record<string, string | number | undefined> - /** - * Appears as a small tooltip on web hover. - */ - title?: string -} & ( - | { - emoji?: true - children: StringChild - } - | { - emoji?: false - children: RNTextProps['children'] - } - ) - -const EMOJI = createEmojiRegex() - -export function childHasEmoji(children: React.ReactNode) { - return (Array.isArray(children) ? children : [children]).some( - child => typeof child === 'string' && createEmojiRegex().test(child), - ) -} - -export function childIsString( - children: React.ReactNode, -): children is StringChild { - return ( - typeof children === 'string' || - (Array.isArray(children) && - children.every(child => typeof child === 'string' || child === null)) - ) -} - -export function renderChildrenWithEmoji( - children: StringChild, - props: Omit<TextProps, 'children'> = {}, -) { - const normalized = Array.isArray(children) ? children : [children] - - return ( - <UITextView {...props}> - {normalized.map(child => { - if (typeof child !== 'string') return child - - const emojis = child.match(EMOJI) - - if (emojis === null) { - return child - } - - return child.split(EMOJI).map((stringPart, index) => ( - <UITextView key={index} {...props}> - {stringPart} - {emojis[index] ? ( - <UITextView - {...props} - style={[props?.style, {color: 'black', fontFamily: 'System'}]}> - {emojis[index]} - </UITextView> - ) : null} - </UITextView> - )) - })} - </UITextView> - ) -} - -/** - * Util to calculate lineHeight from a text size atom and a leading atom - * - * Example: - * `leading(atoms.text_md, atoms.leading_normal)` // => 24 - */ -export function leading< - Size extends {fontSize?: number}, - Leading extends {lineHeight?: number}, ->(textSize: Size, leading: Leading) { - const size = textSize?.fontSize || atoms.text_md.fontSize - const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight - return Math.round(size * lineHeight) -} - -/** - * Ensures that `lineHeight` defaults to a relative value of `1`, or applies - * other relative leading atoms. - * - * If the `lineHeight` value is > 2, we assume it's an absolute value and - * returns it as-is. - */ -export function normalizeTextStyles( - styles: StyleProp<TextStyle>, - { - fontScale, - fontFamily, - }: { - fontScale: number - fontFamily: Alf['fonts']['family'] - } & Pick<Alf, 'flags'>, -) { - const s = flatten(styles) - // should always be defined on these components - s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale - - if (s?.lineHeight) { - if (s.lineHeight !== 0 && s.lineHeight <= 2) { - s.lineHeight = Math.round(s.fontSize * s.lineHeight) - } - } else if (!isNative) { - s.lineHeight = s.fontSize - } - - applyFonts(s, fontFamily) - - return s -} +export type {TextProps} /** * Our main text component. Use this most of the time. @@ -162,10 +37,6 @@ export function Text({ `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`, ) } - - if (emoji && !childIsString(children)) { - logger.error('Text: when <Text emoji />, children can only be strings.') - } } const shared = { @@ -178,12 +49,12 @@ export function Text({ return ( <UITextView {...shared}> - {isIOS && emoji ? renderChildrenWithEmoji(children, shared) : children} + {renderChildrenWithEmoji(children, shared, emoji ?? false)} </UITextView> ) } -export function createHeadingElement({level}: {level: number}) { +function createHeadingElement({level}: {level: number}) { return function HeadingElement({style, ...rest}: TextProps) { const attr = web({ |