diff options
author | Eric Bailey <git@esb.lol> | 2024-02-16 13:25:07 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-16 13:25:07 -0600 |
commit | 1d729721e553848037700688e2f1ccde333a8c84 (patch) | |
tree | 0077aabb7678a34f45e028fe37252aebb26e2587 /src/components/Link.tsx | |
parent | 0ff61e08e94c6b9024a93cd487d2b2df21cd6c65 (diff) | |
download | voidsky-1d729721e553848037700688e2f1ccde333a8c84.tar.zst |
Link updates (#2890)
* Link updates, add atoms * Update comments * Support download * Don't open new window for download
Diffstat (limited to 'src/components/Link.tsx')
-rw-r--r-- | src/components/Link.tsx | 85 |
1 files changed, 53 insertions, 32 deletions
diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 763f07ca9..afd30b5ee 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -13,7 +13,7 @@ import {sanitizeUrl} from '@braintree/sanitize-url' import {useInteractionState} from '#/components/hooks/useInteractionState' import {isWeb} from '#/platform/detection' -import {useTheme, web, flatten, TextStyleProp} from '#/alf' +import {useTheme, web, flatten, TextStyleProp, atoms as a} from '#/alf' import {Button, ButtonProps} from '#/components/Button' import {AllNavigatorParams, NavigationProp} from '#/lib/routes/types' import { @@ -35,6 +35,13 @@ type BaseLinkProps = Pick< Parameters<typeof useLinkProps<AllNavigatorParams>>[0], 'to' > & { + testID?: string + + /** + * Label for a11y. Defaults to the href. + */ + label?: string + /** * The React Navigation `StackAction` to perform when the link is pressed. */ @@ -46,6 +53,18 @@ type BaseLinkProps = Pick< * Note: atm this only works for `InlineLink`s with a string child. */ warnOnMismatchingTextChild?: boolean + + /** + * Callback for when the link is pressed. + * + * DO NOT use this for navigation, that's what the `to` prop is for. + */ + onPress?: (e: GestureResponderEvent) => void + + /** + * Web-only attribute. Sets `download` attr on web. + */ + download?: string } export function useLink({ @@ -53,6 +72,7 @@ export function useLink({ displayText, action = 'push', warnOnMismatchingTextChild, + onPress: outerOnPress, }: BaseLinkProps & { displayText: string }) { @@ -66,6 +86,8 @@ export function useLink({ const onPress = React.useCallback( (e: GestureResponderEvent) => { + outerOnPress?.(e) + const requiresWarning = Boolean( warnOnMismatchingTextChild && displayText && @@ -132,6 +154,7 @@ export function useLink({ displayText, closeModal, openModal, + outerOnPress, ], ) @@ -143,16 +166,7 @@ export function useLink({ } export type LinkProps = Omit<BaseLinkProps, 'warnOnMismatchingTextChild'> & - Omit<ButtonProps, 'style' | 'onPress' | 'disabled' | 'label'> & { - /** - * Label for a11y. Defaults to the href. - */ - label?: string - /** - * Web-only attribute. Sets `download` attr on web. - */ - download?: string - } + Omit<ButtonProps, 'onPress' | 'disabled' | 'label'> /** * A interactive element that renders as a `<a>` tag on the web. On mobile it @@ -166,6 +180,7 @@ export function Link({ children, to, action = 'push', + onPress: outerOnPress, download, ...rest }: LinkProps) { @@ -173,24 +188,26 @@ export function Link({ to, displayText: typeof children === 'string' ? children : '', action, + onPress: outerOnPress, }) return ( <Button label={href} {...rest} + style={[a.justify_start, flatten(rest.style)]} role="link" accessibilityRole="link" href={href} - onPress={onPress} + onPress={download ? undefined : onPress} {...web({ hrefAttrs: { - target: isExternal ? 'blank' : undefined, + target: download ? undefined : isExternal ? 'blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, download, }, dataSet: { - // default to no underline, apply this ourselves + // no underline, only `InlineLink` has underlines noUnderline: '1', }, })}> @@ -200,13 +217,7 @@ export function Link({ } export type InlineLinkProps = React.PropsWithChildren< - BaseLinkProps & - TextStyleProp & { - /** - * Label for a11y. Defaults to the href. - */ - label?: string - } + BaseLinkProps & TextStyleProp > export function InlineLink({ @@ -215,6 +226,8 @@ export function InlineLink({ action = 'push', warnOnMismatchingTextChild, style, + onPress: outerOnPress, + download, ...rest }: InlineLinkProps) { const t = useTheme() @@ -224,18 +237,25 @@ export function InlineLink({ displayText: stringChildren ? children : '', action, warnOnMismatchingTextChild, + onPress: outerOnPress, }) + const { + state: hovered, + onIn: onHoverIn, + onOut: onHoverOut, + } = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() const { state: pressed, onIn: onPressIn, onOut: onPressOut, } = useInteractionState() + const flattenedStyle = flatten(style) return ( <TouchableWithoutFeedback accessibilityRole="button" - onPress={onPress} + onPress={download ? undefined : onPress} onPressIn={onPressIn} onPressOut={onPressOut} onFocus={onFocus} @@ -245,27 +265,28 @@ export function InlineLink({ {...rest} style={[ {color: t.palette.primary_500}, - (focused || pressed) && { + (hovered || focused || pressed) && { outline: 0, textDecorationLine: 'underline', - textDecorationColor: t.palette.primary_500, + textDecorationColor: flattenedStyle.color ?? t.palette.primary_500, }, - flatten(style), + flattenedStyle, ]} role="link" + onMouseEnter={onHoverIn} + onMouseLeave={onHoverOut} accessibilityRole="link" href={href} {...web({ hrefAttrs: { - target: isExternal ? 'blank' : undefined, + target: download ? undefined : isExternal ? 'blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, + download, + }, + dataSet: { + // default to no underline, apply this ourselves + noUnderline: '1', }, - dataSet: stringChildren - ? {} - : { - // default to no underline, apply this ourselves - noUnderline: '1', - }, })}> {children} </Text> |