1import { css } from '@emotion/react'; 2import { breakpoints, spacing } from '@expo/styleguide-base'; 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 className="p-0">{codeBlock}</SnippetContent> 43 </Snippet> 44 ))} 45 </div> 46 ); 47} 48 49const codeBlocksWrapperStyle = css({ 50 display: 'grid', 51 gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', 52 gap: spacing[4], 53 gridAutoRows: '1fr', 54 55 pre: { 56 border: 0, 57 margin: 0, 58 gridTemplateRows: 'minmax(100px, 1fr)', 59 height: '100%', 60 }, 61 62 [`@media screen and (max-width: ${breakpoints.large}px)`]: { 63 gridTemplateColumns: 'minmax(0, 1fr)', 64 gridAutoRows: 'auto', 65 }, 66}); 67 68const codeBlockConnectedWrapperStyle = css({ 69 [`@media screen and (min-width: ${breakpoints.large}px)`]: { 70 gridGap: 0, 71 72 '> div:nth-of-type(odd)': { 73 '> div': { 74 borderRight: 0, 75 borderTopRightRadius: 0, 76 borderBottomRightRadius: 0, 77 }, 78 }, 79 80 '> div:nth-of-type(even)': { 81 '> div': { 82 borderTopLeftRadius: 0, 83 borderBottomLeftRadius: 0, 84 }, 85 }, 86 }, 87}); 88 89const snippetWrapperStyle = css({ 90 [`@media screen and (max-width: ${breakpoints.large}px)`]: { 91 marginBottom: 0, 92 93 '&:last-of-type': { 94 marginBottom: spacing[4], 95 }, 96 }, 97}); 98