import {UITextView} from 'react-native-uitextview' import {logger} from '#/logger' import {atoms as a, type TextStyleProp, useAlf, useTheme, web} from '#/alf' import { childHasEmoji, normalizeTextStyles, renderChildrenWithEmoji, type TextProps, } from '#/alf/typography' export type {TextProps} export {Text as Span} from 'react-native' /** * Our main text component. Use this most of the time. */ export function Text({ children, emoji, style, selectable, title, dataSet, numberOfLines, ...rest }: TextProps) { const {fonts, flags} = useAlf() const t = useTheme() const s = normalizeTextStyles( [ a.text_sm, t.atoms.text, web(numberOfLines === 1 && numberOfLinesClippingFix), style, ], { fontScale: fonts.scaleMultiplier, fontFamily: fonts.family, flags, }, ) if (__DEV__) { if (!emoji && childHasEmoji(children)) { logger.warn( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add '`, ) } } const shared = { uiTextView: true, selectable, numberOfLines, style: s, dataSet: Object.assign({tooltip: title}, dataSet || {}), ...rest, } return ( {renderChildrenWithEmoji(children, shared, emoji ?? false)} ) } function createHeadingElement({level}: {level: number}) { return function HeadingElement({style, ...rest}: TextProps) { const attr = web({ role: 'heading', 'aria-level': level, }) || {} return } } /* * Use semantic components when it's beneficial to the user or to a web scraper */ export const H1 = createHeadingElement({level: 1}) export const H2 = createHeadingElement({level: 2}) export const H3 = createHeadingElement({level: 3}) export const H4 = createHeadingElement({level: 4}) export const H5 = createHeadingElement({level: 5}) export const H6 = createHeadingElement({level: 6}) export function P({style, ...rest}: TextProps) { const attr = web({ role: 'paragraph', }) || {} return ( ) } /** * HACKFIX: React Native Web applies `overflow: hidden` to * text when using the `numberOfLines` prop, which causes it to clip * ascenders/descenders. It only needs to be doing this for the X axis, * so override the style with `overflowX: 'hidden'`. * Note this only works for `numberOfLines={1}` -sfn * * @see https://github.com/necolas/react-native-web/pull/2836 */ const numberOfLinesClippingFix = { overflowY: 'visible', overflowX: 'clip', // mimic browser default behavior of `min-width: 0` on `overflow: hidden` // elements to allow text to shrink smaller than its intrinsic width when // necessary minWidth: 0, // this is neater and supports vertical writing modes, but it's only baseline newly available // overflowInline: 'clip', } satisfies React.CSSProperties as TextStyleProp