1import { css, SerializedStyles } from '@emotion/react'; 2import { theme, typography, spacing, borderRadius } from '@expo/styleguide'; 3import React from 'react'; 4 5import { LinkBase, LinkProps } from './Link'; 6import { TextComponentProps, TextElement } from './types'; 7 8import { durations } from '~/ui/foundations/durations'; 9 10export { LinkBase } from './Link'; 11export { AnchorContext } from './withAnchor'; 12 13export function createTextComponent(Element: TextElement, textStyle?: SerializedStyles) { 14 function TextComponent(props: TextComponentProps) { 15 const { testID, tag, weight: textWeight, theme: textTheme, ...rest } = props; 16 const TextElementTag = tag ?? Element; 17 18 return ( 19 <TextElementTag 20 css={[ 21 baseTextStyle, 22 textStyle, 23 textWeight && typography.utility.weight[textWeight], 24 textTheme && { color: theme.text[textTheme] }, 25 ]} 26 data-testid={testID} 27 {...rest} 28 /> 29 ); 30 } 31 TextComponent.displayName = `Text(${Element})`; 32 return TextComponent; 33} 34 35const baseTextStyle = css({ 36 ...typography.body.paragraph, 37 color: theme.text.default, 38}); 39 40const link = css({ 41 textDecoration: 'none', 42 cursor: 'pointer', 43 44 // transform prevents a 1px shift on hover on Safari 45 transform: 'translate3d(0,0,0)', 46 47 ':hover': { 48 transition: durations.hover, 49 opacity: 0.8, 50 }, 51}); 52 53const listStyle = css({ 54 marginLeft: '1.5rem', 55}); 56 57export const H1 = createTextComponent(TextElement.H1, css(typography.headers.default.h1)); 58export const H2 = createTextComponent(TextElement.H2, css(typography.headers.default.h2)); 59export const H3 = createTextComponent(TextElement.H4, css(typography.headers.default.h3)); 60export const H4 = createTextComponent(TextElement.H4, css(typography.headers.default.h4)); 61export const H5 = createTextComponent(TextElement.H5, css(typography.headers.default.h5)); 62export const H6 = createTextComponent(TextElement.H6, css(typography.headers.default.h6)); 63export const P = createTextComponent(TextElement.P, css(typography.body.paragraph)); 64export const CODE = createTextComponent(TextElement.CODE, css(typography.utility.inlineCode)); 65export const LI = createTextComponent(TextElement.LI, css(typography.body.li)); 66export const LABEL = createTextComponent(TextElement.SPAN, css(typography.body.label)); 67export const HEADLINE = createTextComponent(TextElement.P, css(typography.body.headline)); 68export const FOOTNOTE = createTextComponent(TextElement.P, css(typography.body.footnote)); 69export const CALLOUT = createTextComponent(TextElement.P, css(typography.body.callout)); 70export const BOLD = createTextComponent(TextElement.SPAN, css(typography.utility.weight.semiBold)); 71export const DEMI = createTextComponent(TextElement.SPAN, css(typography.utility.weight.medium)); 72export const UL = createTextComponent(TextElement.UL, css([typography.body.ul, listStyle])); 73export const OL = createTextComponent(TextElement.OL, css([typography.body.ol, listStyle])); 74export const PRE = createTextComponent(TextElement.PRE, css(typography.utility.pre)); 75 76export const kbdStyle = css({ 77 fontFamily: typography.fontFaces.medium, 78 color: theme.text.secondary, 79 padding: `0 ${spacing[1]}px`, 80 boxShadow: `0 0.1rem 0 1px ${theme.border.default}`, 81 borderRadius: borderRadius.small, 82 position: 'relative', 83 display: 'inline-flex', 84 margin: 0, 85 minWidth: 22, 86 justifyContent: 'center', 87 top: -1, 88}); 89 90const isExternalLink = (href?: string) => href?.includes('://'); 91 92export const KBD = createTextComponent(TextElement.KBD, css([typography.utility.pre, kbdStyle])); 93 94export const A = (props: LinkProps & { isStyled?: boolean }) => { 95 const { isStyled, ...rest } = props; 96 return ( 97 <LinkBase 98 css={[link, isStyled && css(typography.utility.anchor)]} 99 openInNewTab={isExternalLink(props.href)} 100 {...rest} 101 /> 102 ); 103}; 104A.displayName = 'Text(a)'; 105