1import { css } from '@emotion/react';
2import { breakpoints, spacing } from '@expo/styleguide';
3import { PropsWithChildren } from 'react';
4
5import { Snippet } from '~/ui/components/Snippet/Snippet';
6import { SnippetContent } from '~/ui/components/Snippet/SnippetContent';
7import { SnippetHeader } from '~/ui/components/Snippet/SnippetHeader';
8
9const MDX_CLASS_NAME_TO_TAB_NAME: Record<string, string> = {
10  'language-swift': 'Swift',
11  'language-kotlin': 'Kotlin',
12  'language-javascript': 'JavaScript',
13  'language-typescript': 'TypeScript',
14  'language-json': 'JSON',
15  'language-ruby': 'Ruby',
16  'language-groovy': 'Gradle',
17};
18
19type Props = PropsWithChildren<{
20  tabs?: string[];
21  connected?: boolean;
22}>;
23
24export function CodeBlocksTable({ children, tabs, connected = true, ...rest }: Props) {
25  const childrenArray = Array.isArray(children) ? children : [children];
26  const codeBlocks = childrenArray.filter(
27    ({ props }) =>
28      props.children.props.className && props.children.props.className.startsWith('language-')
29  );
30  const tabNames =
31    tabs ||
32    codeBlocks.map(child => {
33      const className = child.props.children.props.className;
34      return MDX_CLASS_NAME_TO_TAB_NAME[className] || className.replace('language-', '');
35    });
36
37  return (
38    <div css={[codeBlocksWrapperStyle, connected && codeBlockConnectedWrapperStyle]} {...rest}>
39      {codeBlocks.map((codeBlock, index) => (
40        <Snippet key={index} css={snippetWrapperStyle}>
41          <SnippetHeader title={tabNames[index]} />
42          <SnippetContent skipPadding css={snippetContentStyle}>
43            {codeBlock}
44          </SnippetContent>
45        </Snippet>
46      ))}
47    </div>
48  );
49}
50
51const codeBlocksWrapperStyle = css({
52  display: 'grid',
53  gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)',
54  gap: spacing[4],
55  gridAutoRows: '1fr',
56
57  pre: {
58    border: 0,
59    margin: 0,
60    gridTemplateRows: 'minmax(100px, 1fr)',
61    height: '100%',
62  },
63
64  [`@media screen and (max-width: ${breakpoints.large}px)`]: {
65    gridTemplateColumns: 'minmax(0, 1fr)',
66    gridAutoRows: 'auto',
67  },
68});
69
70const codeBlockConnectedWrapperStyle = css({
71  [`@media screen and (min-width: ${breakpoints.large}px)`]: {
72    gridGap: 0,
73
74    '> div:nth-of-type(odd)': {
75      '> div': {
76        borderRight: 0,
77        borderTopRightRadius: 0,
78        borderBottomRightRadius: 0,
79      },
80    },
81
82    '> div:nth-of-type(even)': {
83      '> div': {
84        borderTopLeftRadius: 0,
85        borderBottomLeftRadius: 0,
86      },
87    },
88  },
89});
90
91const snippetWrapperStyle = css({
92  [`@media screen and (max-width: ${breakpoints.large}px)`]: {
93    marginBottom: 0,
94
95    '&:last-of-type': {
96      marginBottom: spacing[4],
97    },
98  },
99});
100
101const snippetContentStyle = css({
102  height: '100%',
103});
104