Last active
          April 26, 2020 23:26 
        
      - 
      
- 
        Save steida/47c0500e3607d50a548ddcecf0bc409c to your computer and use it in GitHub Desktop. 
Revisions
- 
        steida revised this gist Apr 26, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewingThis file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -133,7 +133,7 @@ const styles = StyleSheet.create({ }); // Next.js needs ref for Intersection Observer based prefetching. // That's how we delegate it to React Native for Web Text component. const RNTextWithDOMRef = forwardRef( (props: RNTextProps & { children: ReactNode }, ref) => { return ( 
- 
        steida created this gist Apr 26, 2020 .There are no files selected for viewingThis file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,224 @@ import Link, { LinkProps } from 'next/link'; import React, { forwardRef, ReactNode, useCallback, useMemo, useState, } from 'react'; import { StyleSheet, Text as RNText, TextProps as RNTextProps, TouchableOpacity, TouchableOpacityProps, } from 'react-native'; 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, }, button: { ...theme.fontSize.medium, backgroundColor: theme.color.green, borderRadius: theme.spacing.smaller, color: theme.color.foreground, fontWeight: theme.fontWeight.medium, marginVertical: theme.spacing.smaller, paddingHorizontal: theme.spacing.small, paddingVertical: theme.spacing.smaller, textAlign: 'center', }, smallButton: { ...theme.fontSize.small, backgroundColor: theme.color.green, borderRadius: theme.spacing.smaller, color: theme.color.foreground, fontWeight: theme.fontWeight.medium, paddingHorizontal: theme.spacing.small, textAlign: 'center', }, }); /** * It's like Next LinkProps except href is optional and passHref with prefetch are * 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 spread. */ interface NextLinkSomeProps { href?: LinkProps['href']; as?: LinkProps['as']; replace?: LinkProps['replace']; scroll?: LinkProps['scroll']; shallow?: LinkProps['shallow']; // 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; } // Only props I use. interface TouchableOpacitySomeProps { onPress?: TouchableOpacityProps['onPress']; onPressIn?: TouchableOpacityProps['onPressIn']; disabled?: TouchableOpacityProps['disabled']; } interface TextOwnProps { variant: keyof ReturnType<typeof createStyles>; color?: keyof Theme['color']; children: ReactNode; } export type TextProps = RNTextProps & NextLinkSomeProps & TouchableOpacitySomeProps & TextOwnProps; const styles = StyleSheet.create({ linkHover: { textDecorationLine: 'underline', }, }); // Next.js needs ref for Intersection Observer based prefetching. // That's how we delegate it to React Native or Web Text component. const RNTextWithDOMRef = forwardRef( (props: RNTextProps & { children: ReactNode }, ref) => { return ( <RNText {...props} // @ts-ignore RNfW prop. forwardedRef={ref} /> ); }, ); export const Text = ({ // TextNextLinkProps href, as, replace, scroll, shallow, // TouchableOpacitySomeProps onPress, onPressIn, disabled, // 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', onMouseEnter, onMouseLeave, })} style={[ variantStyle, colorStyle, props.style, hasHover && styles.linkHover, ]} > {children || '…'} </RNTextWithDOMRef> ); // TODO: Handle external hrefs, use Platform.select({ web: { href, target: '_blank' } }). if (href) return ( <Link {...{ href, as, replace, scroll, shallow, passHref: true }}> {text} </Link> ); if (onPress || onPressIn || disabled) { return ( <TouchableOpacity accessibilityRole="button" {...{ onPress, onPressIn, disabled }} {...(disabled && { style: { opacity: theme.opacity.disabled } })} > {text} </TouchableOpacity> ); } return text; };