mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {StyleProp, TextProps as RNTextProps, TextStyle} from 'react-native'
3import {UITextView} from 'react-native-uitextview'
4
5import {isNative} from '#/platform/detection'
6import {atoms, flatten, useTheme, web} from '#/alf'
7
8export type TextProps = RNTextProps & {
9 /**
10 * Lets the user select text, to use the native copy and paste functionality.
11 */
12 selectable?: boolean
13}
14
15/**
16 * Util to calculate lineHeight from a text size atom and a leading atom
17 *
18 * Example:
19 * `leading(atoms.text_md, atoms.leading_normal)` // => 24
20 */
21export function leading<
22 Size extends {fontSize?: number},
23 Leading extends {lineHeight?: number},
24>(textSize: Size, leading: Leading) {
25 const size = textSize?.fontSize || atoms.text_md.fontSize
26 const lineHeight = leading?.lineHeight || atoms.leading_normal.lineHeight
27 return Math.round(size * lineHeight)
28}
29
30/**
31 * Ensures that `lineHeight` defaults to a relative value of `1`, or applies
32 * other relative leading atoms.
33 *
34 * If the `lineHeight` value is > 2, we assume it's an absolute value and
35 * returns it as-is.
36 */
37export function normalizeTextStyles(styles: StyleProp<TextStyle>) {
38 const s = flatten(styles)
39 // should always be defined on these components
40 const fontSize = s.fontSize || atoms.text_md.fontSize
41
42 if (s?.lineHeight) {
43 if (s.lineHeight !== 0 && s.lineHeight <= 2) {
44 s.lineHeight = Math.round(fontSize * s.lineHeight)
45 }
46 } else if (!isNative) {
47 s.lineHeight = s.fontSize
48 }
49
50 return s
51}
52
53/**
54 * Our main text component. Use this most of the time.
55 */
56export function Text({style, selectable, ...rest}: TextProps) {
57 const t = useTheme()
58 const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)])
59
60 return <UITextView selectable={selectable} uiTextView style={s} {...rest} />
61}
62
63export function createHeadingElement({level}: {level: number}) {
64 return function HeadingElement({style, ...rest}: TextProps) {
65 const attr =
66 web({
67 role: 'heading',
68 'aria-level': level,
69 }) || {}
70 return <Text {...attr} {...rest} style={style} />
71 }
72}
73
74/*
75 * Use semantic components when it's beneficial to the user or to a web scraper
76 */
77export const H1 = createHeadingElement({level: 1})
78export const H2 = createHeadingElement({level: 2})
79export const H3 = createHeadingElement({level: 3})
80export const H4 = createHeadingElement({level: 4})
81export const H5 = createHeadingElement({level: 5})
82export const H6 = createHeadingElement({level: 6})
83export function P({style, ...rest}: TextProps) {
84 const attr =
85 web({
86 role: 'paragraph',
87 }) || {}
88 return (
89 <Text
90 {...attr}
91 {...rest}
92 style={[atoms.text_md, atoms.leading_normal, flatten(style)]}
93 />
94 )
95}