xref: /expo/docs/ui/components/BoxLink/index.tsx (revision b0159875)
1import { css } from '@emotion/react';
2import {
3  borderRadius,
4  spacing,
5  theme,
6  ArrowRightIcon,
7  iconSize,
8  shadows,
9  ArrowUpRightIcon,
10} from '@expo/styleguide';
11import type { IconProps } from '@expo/styleguide/dist/types';
12import React, { ComponentType, PropsWithChildren, ReactNode } from 'react';
13
14import { A, HEADLINE, P } from '~/ui/components/Text';
15
16type BoxLinkProps = PropsWithChildren<{
17  title: string;
18  description: string | ReactNode;
19  href?: string;
20  testID?: string;
21  Icon?: ComponentType<IconProps>;
22}>;
23
24export function BoxLink({ title, description, href, testID, Icon }: BoxLinkProps) {
25  const isExternal = Boolean(href && href.startsWith('http'));
26  const ArrowIcon = isExternal ? ArrowUpRightIcon : ArrowRightIcon;
27  return (
28    <A href={href} css={tileContainerStyle} data-testid={testID} openInNewTab={isExternal}>
29      <div css={tileContentWrapperStyle}>
30        {Icon && (
31          <div css={tileIconBackgroundStyle}>
32            <Icon />
33          </div>
34        )}
35        <div>
36          <HEADLINE tag="span">{title}</HEADLINE>
37          <P>{description}</P>
38        </div>
39      </div>
40      <ArrowIcon css={arrowIconStyle} color={theme.icon.secondary} />
41    </A>
42  );
43}
44
45const tileContainerStyle = css({
46  display: 'flex',
47  flexDirection: 'row',
48  justifyContent: 'space-between',
49  border: `1px solid ${theme.border.default}`,
50  borderRadius: borderRadius.medium,
51  padding: `${spacing[3]}px ${spacing[4]}px`,
52  marginBottom: spacing[3],
53
54  ':hover': {
55    boxShadow: shadows.micro,
56  },
57});
58
59const tileContentWrapperStyle = css({
60  display: 'flex',
61  flexDirection: 'row',
62  gap: spacing[4],
63});
64
65const tileIconBackgroundStyle = css({
66  display: 'flex',
67  backgroundColor: theme.background.tertiary,
68  borderRadius: borderRadius.medium,
69  alignSelf: 'center',
70  alignItems: 'center',
71  justifyContent: 'center',
72  width: 36,
73  height: 36,
74});
75
76const arrowIconStyle = css({
77  alignSelf: 'center',
78  alignContent: 'flex-end',
79  minWidth: iconSize.regular,
80  marginLeft: spacing[3],
81});
82