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/alf/typography.tsx | |
parent | 523d1f01a51c0e85e49916fb42b204f7004ffac1 (diff) | |
parent | b4d07c4112b9a62b5380948051aa4a7fd391a2d4 (diff) | |
download | voidsky-32bf8122e8c8a0fbadd53b8a015cfbc9014519a2.tar.zst |
Merge branch 'main' into main
Diffstat (limited to 'src/alf/typography.tsx')
-rw-r--r-- | src/alf/typography.tsx | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx new file mode 100644 index 000000000..1b1e81bd4 --- /dev/null +++ b/src/alf/typography.tsx @@ -0,0 +1,126 @@ +import React, {Children} from 'react' +import {TextProps as RNTextProps} from 'react-native' +import {StyleProp, TextStyle} from 'react-native' +import {UITextView} from 'react-native-uitextview' +import createEmojiRegex from 'emoji-regex' + +import {isNative} from '#/platform/detection' +import {isIOS} from '#/platform/detection' +import {Alf, applyFonts, atoms, flatten} from '#/alf' + +/** + * 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 StringChild = string | (string | null)[] +export type TextProps = RNTextProps & { + /** + * 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 + /** + * Whether the children could possibly contain emoji. + */ + emoji?: boolean +} + +const EMOJI = createEmojiRegex() + +export function childHasEmoji(children: React.ReactNode) { + let hasEmoji = false + Children.forEach(children, child => { + if (typeof child === 'string' && createEmojiRegex().test(child)) { + hasEmoji = true + } + }) + return hasEmoji +} + +export function renderChildrenWithEmoji( + children: React.ReactNode, + props: Omit<TextProps, 'children'> = {}, + emoji: boolean, +) { + if (!isIOS || !emoji) { + return children + } + return Children.map(children, child => { + if (typeof child !== 'string') return child + + const emojis = child.match(EMOJI) + + if (emojis === null) { + return child + } + + return child.split(EMOJI).map((stringPart, index) => [ + stringPart, + emojis[index] ? ( + <UITextView + {...props} + style={[props?.style, {color: 'black', fontFamily: 'System'}]}> + {emojis[index]} + </UITextView> + ) : null, + ]) + }) +} + +const SINGLE_EMOJI_RE = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u +export function isOnlyEmoji(text: string) { + return text.length <= 15 && SINGLE_EMOJI_RE.test(text) +} |