Link updates (#2890)

* Link updates, add atoms

* Update comments

* Support download

* Don't open new window for download
zio/stable
Eric Bailey 2024-02-16 13:25:07 -06:00 committed by GitHub
parent 0ff61e08e9
commit 1d729721e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 33 deletions

View File

@ -122,6 +122,9 @@ export const atoms = {
flex_shrink: {
flexShrink: 1,
},
justify_start: {
justifyContent: 'flex-start',
},
justify_center: {
justifyContent: 'center',
},
@ -140,10 +143,31 @@ export const atoms = {
align_end: {
alignItems: 'flex-end',
},
self_auto: {
alignSelf: 'auto',
},
self_start: {
alignSelf: 'flex-start',
},
self_end: {
alignSelf: 'flex-end',
},
self_center: {
alignSelf: 'center',
},
self_stretch: {
alignSelf: 'stretch',
},
self_baseline: {
alignSelf: 'baseline',
},
/*
* Text
*/
text_left: {
textAlign: 'left',
},
text_center: {
textAlign: 'center',
},
@ -195,10 +219,16 @@ export const atoms = {
font_bold: {
fontWeight: tokens.fontWeight.semibold,
},
italic: {
fontStyle: 'italic',
},
/*
* Border
*/
border_0: {
borderWidth: 0,
},
border: {
borderWidth: 1,
},
@ -208,6 +238,12 @@ export const atoms = {
border_b: {
borderBottomWidth: 1,
},
border_l: {
borderLeftWidth: 1,
},
border_r: {
borderRightWidth: 1,
},
/*
* Shadow

View File

@ -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>

View File

@ -19,9 +19,14 @@ export function Links() {
style={[a.text_md]}>
External
</InlineLink>
<InlineLink to="https://bsky.social" style={[a.text_md]}>
<InlineLink to="https://bsky.social" style={[a.text_md, t.atoms.text]}>
<H3>External with custom children</H3>
</InlineLink>
<InlineLink
to="https://bsky.social"
style={[a.text_md, t.atoms.text_contrast_low]}>
External with custom children
</InlineLink>
<InlineLink
to="https://bsky.social"
warnOnMismatchingTextChild