xref: /expo/docs/ui/components/Home/Cells.tsx (revision 5959d3ad)
1import { css } from '@emotion/react';
2import { shadows, theme, typography } from '@expo/styleguide';
3import { borderRadius, breakpoints, palette, spacing } from '@expo/styleguide-base';
4import { ArrowRightIcon, ArrowUpRightIcon } from '@expo/styleguide-icons';
5import { PropsWithChildren } from 'react';
6import { Container, Col, ColProps } from 'react-grid-system';
7
8import { A, CALLOUT, LABEL, P } from '~/ui/components/Text';
9
10export const CellContainer = ({ children }: PropsWithChildren<object>) => (
11  // https://github.com/sealninja/react-grid-system/issues/175
12  <Container fluid style={{ paddingLeft: -15, paddingRight: -15, marginBottom: spacing[6] }}>
13    {children}
14  </Container>
15);
16
17const CustomCol = ({ children, sm, md, lg, xl, xxl }: PropsWithChildren<ColProps>) => (
18  <>
19    <Col css={cellWrapperStyle} sm={sm} md={md} lg={lg} xl={xl} xxl={xxl}>
20      {children}
21    </Col>
22    <div css={mobileCellWrapperStyle}>{children}</div>
23  </>
24);
25
26export const GridCell = ({
27  children,
28  sm,
29  md,
30  lg,
31  xl,
32  xxl,
33  className,
34}: PropsWithChildren<ColProps>) => (
35  <CustomCol css={cellWrapperStyle} sm={sm} md={md} lg={lg} xl={xl} xxl={xxl}>
36    <div css={cellStyle} className={className}>
37      {children}
38    </div>
39  </CustomCol>
40);
41
42type APIGridCellProps = ColProps & {
43  icon?: string | JSX.Element;
44  title?: string;
45  link?: string;
46};
47
48export const APIGridCell = ({
49  icon,
50  title,
51  link,
52  className,
53  sm = 6,
54  md = 6,
55  lg = 6,
56  xl = 3,
57}: APIGridCellProps) => (
58  <CustomCol css={cellWrapperStyle} md={md} sm={sm} lg={lg} xl={xl}>
59    <A href={link} css={[cellStyle, cellAPIStyle, cellHoverStyle]} className={className} isStyled>
60      <div css={cellIconWrapperStyle}>{icon}</div>
61      <LABEL css={cellTitleWrapperStyle}>
62        {title}
63        <ArrowRightIcon className="text-icon-secondary" />
64      </LABEL>
65    </A>
66  </CustomCol>
67);
68
69type TalkGridCellProps = ColProps & {
70  title?: string;
71  event?: string;
72  description?: string;
73  videoId?: string;
74};
75
76export const TalkGridCell = ({
77  title,
78  event,
79  description,
80  videoId,
81  className,
82  sm = 6,
83  md = 6,
84  lg = 6,
85  xl = 3,
86}: TalkGridCellProps) => (
87  <CustomCol css={cellWrapperStyle} md={md} sm={sm} lg={lg} xl={xl}>
88    <A
89      openInNewTab
90      href={`https://www.youtube.com/watch?v=${videoId}`}
91      css={[cellStyle, cellAPIStyle, cellHoverStyle]}
92      className={className}
93      isStyled>
94      <img
95        src={`https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
96        alt="Thumbnail"
97        className="border-b border-b-default"
98      />
99      <div css={cellTitleWrapperStyle} className="gap-1">
100        <div>
101          <LABEL className="block !leading-normal !mb-1">{title}</LABEL>
102          <CALLOUT theme="secondary">{description}</CALLOUT>
103          <CALLOUT theme="secondary">{event}</CALLOUT>
104        </div>
105        <ArrowUpRightIcon className="text-icon-secondary shrink-0" />
106      </div>
107    </A>
108  </CustomCol>
109);
110
111type CommunityGridCellProps = APIGridCellProps & {
112  description?: string;
113  iconBackground?: string;
114};
115
116export const CommunityGridCell = ({
117  icon,
118  iconBackground = palette.light.gray11,
119  title,
120  link,
121  description,
122  className,
123  md = 6,
124}: CommunityGridCellProps) => (
125  <CustomCol css={cellWrapperStyle} md={md}>
126    <A
127      href={link}
128      css={[cellStyle, cellCommunityStyle, cellCommunityHoverStyle]}
129      className={className}
130      isStyled>
131      <div css={[cellCommunityIconWrapperStyle, css({ backgroundColor: iconBackground })]}>
132        {icon}
133      </div>
134      <div css={cellCommunityContentStyle}>
135        <span css={cellCommunityTitleStyle}>{title}</span>
136        <P css={cellCommunityDescriptionStyle}>{description}</P>
137      </div>
138      <ArrowUpRightIcon className="text-icon-secondary self-center ml-1.5" />
139    </A>
140  </CustomCol>
141);
142
143const cellWrapperStyle = css`
144  padding-left: 0 !important;
145  padding-right: 0 !important;
146
147  @media screen and (max-width: ${breakpoints.medium}px) {
148    display: none;
149  }
150`;
151
152const mobileCellWrapperStyle = css({
153  width: '100%',
154
155  [`@media screen and (min-width: ${breakpoints.medium}px)`]: {
156    display: 'none',
157  },
158});
159
160const cellHoverStyle = css`
161  & {
162    transition: box-shadow 200ms;
163
164    svg {
165      transition: transform 200ms;
166    }
167  }
168
169  &:hover {
170    box-shadow: ${shadows.sm};
171
172    svg {
173      transform: scale(1.05);
174    }
175
176    svg[role='img'] {
177      transform: none;
178    }
179  }
180`;
181
182const cellStyle = css({
183  margin: spacing[4],
184  padding: spacing[8],
185  minHeight: 200,
186  overflow: 'hidden',
187  position: 'relative',
188  borderWidth: 1,
189  borderStyle: 'solid',
190  borderColor: theme.border.default,
191  borderRadius: borderRadius.lg,
192
193  h2: {
194    marginTop: 0,
195    marginBottom: 0,
196  },
197
198  h3: {
199    marginTop: 0,
200  },
201});
202
203const cellAPIStyle = css({
204  display: 'block',
205  backgroundColor: theme.background.subtle,
206  padding: 0,
207  overflow: 'hidden',
208  textDecoration: 'none',
209});
210
211const cellIconWrapperStyle = css({
212  display: 'flex',
213  minHeight: 142,
214  justifyContent: 'space-around',
215  alignItems: 'center',
216});
217
218const cellTitleWrapperStyle = css({
219  display: 'flex',
220  justifyContent: 'space-between',
221  backgroundColor: theme.background.default,
222  padding: spacing[4],
223  textDecoration: 'none',
224  minHeight: 30,
225  color: theme.text.default,
226  alignItems: 'center',
227});
228
229const cellCommunityStyle = css({
230  display: 'flex',
231  minHeight: 'unset',
232  padding: spacing[4],
233  margin: `${spacing[3]}px ${spacing[4]}px`,
234  flexDirection: 'row',
235  textDecoration: 'none',
236});
237
238const cellCommunityIconWrapperStyle = css({
239  height: 48,
240  width: 48,
241  minWidth: 48,
242  display: 'flex',
243  justifyContent: 'center',
244  alignItems: 'center',
245  borderRadius: borderRadius.lg,
246  marginRight: spacing[3],
247});
248
249const cellCommunityContentStyle = css({
250  flexGrow: 1,
251});
252
253const cellCommunityTitleStyle = css({
254  ...typography.fontSizes[16],
255  fontWeight: 500,
256  color: theme.text.default,
257  textDecoration: 'none',
258  marginBottom: spacing[2],
259});
260
261const cellCommunityDescriptionStyle = css({
262  ...typography.fontSizes[14],
263  color: theme.text.secondary,
264});
265
266const cellCommunityHoverStyle = css`
267  & {
268    transition: box-shadow 200ms;
269
270    svg {
271      transition: transform 200ms;
272    }
273  }
274
275  &:hover {
276    box-shadow: ${shadows.sm};
277
278    svg {
279      transform: scale(1.075);
280    }
281  }
282`;
283