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 | |
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
-rw-r--r-- | src/alf/atoms.ts | 36 | ||||
-rw-r--r-- | src/components/Link.tsx | 85 | ||||
-rw-r--r-- | src/view/screens/Storybook/Links.tsx | 7 |
3 files changed, 95 insertions, 33 deletions
diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index bbf7e3243..f75e8ffe0 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -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 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> diff --git a/src/view/screens/Storybook/Links.tsx b/src/view/screens/Storybook/Links.tsx index 2828e74bf..3f1806906 100644 --- a/src/view/screens/Storybook/Links.tsx +++ b/src/view/screens/Storybook/Links.tsx @@ -19,11 +19,16 @@ 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 style={[a.text_lg]}> https://bsky.social |