feat: add a useLinkProps hook

This commit is contained in:
Satyajit Sahoo
2020-04-27 13:45:51 +02:00
parent 942d2be2c7
commit f2291d110f
4 changed files with 82 additions and 48 deletions

View File

@@ -1,10 +1,7 @@
import * as React from 'react';
import { Text, TextProps, Platform, GestureResponderEvent } from 'react-native';
import {
NavigationAction,
NavigationHelpersContext,
} from '@react-navigation/core';
import useLinkTo from './useLinkTo';
import { Text, TextProps, GestureResponderEvent } from 'react-native';
import { NavigationAction } from '@react-navigation/core';
import useLinkProps from './useLinkProps';
type Props = {
to: string;
@@ -20,9 +17,8 @@ type Props = {
* @param props.action Optional action to use for in-page navigation. By default, the path is parsed to an action based on linking config.
* @param props.children Child elements to render the content.
*/
export default function Link({ to, action, children, ...rest }: Props) {
const navigation = React.useContext(NavigationHelpersContext);
const linkTo = useLinkTo();
export default function Link({ to, action, ...rest }: Props) {
const props = useLinkProps({ to, action });
const onPress = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
@@ -32,44 +28,16 @@ export default function Link({ to, action, children, ...rest }: Props) {
rest.onPress?.(e);
}
let shouldHandle = false;
if (Platform.OS !== 'web' || !event) {
shouldHandle = event ? !e.defaultPrevented : true;
} else if (
!e.defaultPrevented && // onPress prevented default
// @ts-ignore
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
// @ts-ignore
(e.button == null || e.button === 0) && // ignore everything but left clicks
(rest.target == null || rest.target === '_self') // let browser handle "target=_blank" etc.
) {
event.preventDefault();
shouldHandle = true;
}
if (shouldHandle) {
if (action) {
if (navigation) {
navigation.dispatch(action);
} else {
throw new Error("Couldn't find a navigation object.");
}
} else {
linkTo(to);
}
if (props.onClick) {
props.onClick(e);
} else {
props.onPress(e);
}
};
const props = {
href: to,
accessibilityRole: 'link' as const,
...Platform.select({
web: { onClick: onPress } as any,
default: { onPress },
}),
return React.createElement(Text, {
...props,
...rest,
};
return <Text {...props}>{children}</Text>;
...(props.onClick ? { onClick: onPress } : { onPress }),
});
}

View File

@@ -13,4 +13,5 @@ export { default as useTheme } from './theming/useTheme';
export { default as Link } from './Link';
export { default as useLinking } from './useLinking';
export { default as useLinkTo } from './useLinkTo';
export { default as useLinkProps } from './useLinkProps';
export { default as useLinkBuilder } from './useLinkBuilder';

View File

@@ -0,0 +1,65 @@
import * as React from 'react';
import { Platform, GestureResponderEvent } from 'react-native';
import {
NavigationAction,
NavigationHelpersContext,
} from '@react-navigation/core';
import useLinkTo from './useLinkTo';
type Props = {
to: string;
action?: NavigationAction;
};
/**
* Hook to get props for an anchor tag so it can work with in page navigation.
*
* @param props.to Absolute path to screen (e.g. `/feeds/hot`).
* @param props.action Optional action to use for in-page navigation. By default, the path is parsed to an action based on linking config.
*/
export default function useLinkProps({ to, action }: Props) {
const navigation = React.useContext(NavigationHelpersContext);
const linkTo = useLinkTo();
const onPress = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
) => {
let shouldHandle = false;
if (Platform.OS !== 'web' || !e) {
shouldHandle = e ? !e.defaultPrevented : true;
} else if (
!e.defaultPrevented && // onPress prevented default
// @ts-ignore
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
// @ts-ignore
(e.button == null || e.button === 0) && // ignore everything but left clicks
// @ts-ignore
[undefined, null, '', 'self'].includes(e.currentTarget?.target) // let browser handle "target=_blank" etc.
) {
e.preventDefault();
shouldHandle = true;
}
if (shouldHandle) {
if (action) {
if (navigation) {
navigation.dispatch(action);
} else {
throw new Error("Couldn't find a navigation object.");
}
} else {
linkTo(to);
}
}
};
return {
href: to,
accessibilityRole: 'link' as const,
...Platform.select({
web: { onClick: onPress } as any,
default: { onPress },
}),
};
}