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