Skip to content

Instantly share code, notes, and snippets.

@steida
Created April 26, 2020 02:46
Show Gist options
  • Save steida/65f672c4962b39eb02c5201ffc4e4121 to your computer and use it in GitHub Desktop.
Save steida/65f672c4962b39eb02c5201ffc4e4121 to your computer and use it in GitHub Desktop.
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