Created
April 26, 2020 02:46
-
-
Save steida/65f672c4962b39eb02c5201ffc4e4121 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Link from 'next/link'; | |
import React, { | |
forwardRef, | |
ReactNode, | |
useCallback, | |
useMemo, | |
useState, | |
} from 'react'; | |
import { | |
StyleSheet, | |
Text as RNText, | |
TextProps as RNTextProps, | |
} from 'react-native'; | |
import { UrlObject } from 'url'; | |
import { useStyles } from '../hooks/useStyles'; | |
import { useTheme } from '../hooks/useTheme'; | |
import { Theme } from '../themes/theme'; | |
const createStyles = (theme: Theme) => ({ | |
h1: { | |
...theme.fontSize.big, | |
color: theme.color.background, | |
marginBottom: theme.spacing.medium, | |
}, | |
h2: { | |
...theme.fontSize.medium, | |
color: theme.color.background, | |
fontWeight: theme.fontWeight.medium, | |
marginBottom: theme.spacing.medium, | |
}, | |
text: { | |
...theme.fontSize.medium, | |
color: theme.color.background, | |
}, | |
p: { | |
...theme.fontSize.medium, | |
color: theme.color.background, | |
marginBottom: theme.spacing.medium, | |
}, | |
small: { | |
...theme.fontSize.small, | |
color: theme.color.background, | |
}, | |
smallRed: { | |
...theme.fontSize.small, | |
color: theme.color.red, | |
}, | |
smallBold: { | |
...theme.fontSize.small, | |
color: theme.color.background, | |
fontWeight: theme.fontWeight.medium, | |
}, | |
smallBoldRed: { | |
...theme.fontSize.small, | |
color: theme.color.red, | |
fontWeight: theme.fontWeight.medium, | |
}, | |
li: { | |
...theme.fontSize.medium, | |
color: theme.color.background, | |
marginBottom: theme.spacing.small, | |
}, | |
link: { | |
...theme.fontSize.medium, | |
color: theme.color.blue, | |
}, | |
code: { | |
...theme.fontSize.small, | |
fontFamily: theme.fontFamily.monospace, | |
}, | |
// Consolas, ‘Andale Mono WT’, ‘Andale Mono’, ‘Lucida Console’, ‘Lucida Sans Typewriter’, ‘DejaVu Sans Mono’, ‘Bitstream Vera Sans Mono’, ‘Liberation Mono’, ‘Nimbus Mono L’, Monaco, ‘Courier New’, Courier, monospace | |
}); | |
/** | |
* It's like Next LinkProps except href is optional and passHref with prefetch not allowed. | |
* The idea is simple. Because anchor is rendered via Text in RNfW, | |
* we just added Next Link functionality into Text. | |
* We had to copy-paste LinkProps, because props must be handled explicitly. | |
*/ | |
interface TextNextLinkProps { | |
href?: string | UrlObject; | |
as?: string | UrlObject; | |
replace?: boolean; | |
scroll?: boolean; | |
shallow?: boolean; | |
// Next.js auto-prefetches automatically based on viewport. The prefetch attribute is | |
// no longer needed. More: https://err.sh/zeit/next.js/prefetch-true-deprecated | |
// prefetch?: boolean; | |
} | |
interface TextOwnProps { | |
variant: keyof ReturnType<typeof createStyles>; | |
color?: keyof Theme['color']; | |
children: ReactNode; | |
} | |
export type TextProps = RNTextProps & TextNextLinkProps & TextOwnProps; | |
const styles = StyleSheet.create({ | |
linkHover: { | |
textDecorationLine: 'underline', | |
}, | |
}); | |
const RNTextWithDOMRef = forwardRef( | |
(props: RNTextProps & { children: ReactNode }, ref) => { | |
return ( | |
<RNText | |
{...props} | |
// @ts-ignore RNfW props. | |
forwardedRef={ref} | |
/> | |
); | |
}, | |
); | |
// TODO: isExternal auto-detection then Platform.select({ web: { href, target: '_blank' } }). | |
export const Text = ({ | |
// TextNextLinkProps | |
href, | |
as, | |
replace, | |
scroll, | |
shallow, | |
// TextOwnProps | |
variant, | |
color, | |
children, | |
...props | |
}: TextProps) => { | |
const variantStyle = useStyles(createStyles)[variant]; | |
const theme = useTheme(); | |
const colorStyle = useMemo( | |
() => | |
color && | |
StyleSheet.create({ color: { color: theme.color[color] } }).color, | |
[color, theme.color], | |
); | |
const [hasHover, setHasHover] = useState(false); | |
const onMouseEnter = useCallback(() => { | |
setHasHover(true); | |
}, []); | |
const onMouseLeave = useCallback(() => { | |
setHasHover(false); | |
}, []); | |
const text = ( | |
<RNTextWithDOMRef | |
{...props} | |
{...(href && { | |
accessibilityRole: 'link', | |
href, | |
onMouseEnter, | |
onMouseLeave, | |
})} | |
style={[ | |
variantStyle, | |
colorStyle, | |
props.style, | |
hasHover && styles.linkHover, | |
]} | |
> | |
{children || '…'} | |
</RNTextWithDOMRef> | |
); | |
if (!href) return text; | |
return <Link {...{ href, as, replace, scroll, shallow }}>{text}</Link>; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment