1import { css, Global } from '@emotion/react'; 2import { SerializedStyles } from '@emotion/serialize'; 3import { breakpoints, spacing, theme } from '@expo/styleguide'; 4import React, { PropsWithChildren, ReactNode } from 'react'; 5 6import { Sidebar } from '../Sidebar'; 7 8import { Header } from '~/ui/components/Header'; 9import { LayoutScroll } from '~/ui/components/Layout'; 10 11type LayoutProps = PropsWithChildren<{ 12 /** 13 * The content within the top bar that spans the columns. 14 */ 15 header?: ReactNode; 16 /** 17 * The content within the left column. 18 */ 19 navigation?: ReactNode; 20 /** 21 * The content within the right column. 22 */ 23 sidebar?: ReactNode; 24 /** 25 * Custom CSS for the whole layout. 26 */ 27 cssLayout?: SerializedStyles; 28 /** 29 * Custom CSS for the main content wrapper. 30 */ 31 cssContent?: SerializedStyles; 32}>; 33 34export const Layout = ({ 35 // note(simek): stub props for now, until we don't use new Layout 36 header = ( 37 <Header 38 sidebar={<Sidebar />} 39 sidebarActiveGroup="general" 40 isMobileMenuVisible={false} 41 setMobileMenuVisible={() => undefined} 42 /> 43 ), 44 navigation, 45 sidebar, 46 children, 47 cssLayout = undefined, 48 cssContent = undefined, 49}: LayoutProps) => ( 50 <> 51 <Global 52 styles={css({ 53 // Ensure correct background for Overscroll 54 '[data-expo-theme="dark"] body': { 55 backgroundColor: theme.background.screen, 56 }, 57 })} 58 /> 59 <header css={headerStyle}>{header}</header> 60 <main css={[layoutStyle, cssLayout]}> 61 {navigation && <nav css={navigationStyle}>{navigation}</nav>} 62 <LayoutScroll> 63 <article css={[innerContentStyle, cssContent]}>{children}</article> 64 </LayoutScroll> 65 {sidebar && <aside css={asideStyle}>{sidebar}</aside>} 66 </main> 67 </> 68); 69 70const HEADER_HEIGHT = 60; 71 72const layoutStyle = css({ 73 display: 'flex', 74 alignItems: 'stretch', 75 maxHeight: `calc(100vh - ${HEADER_HEIGHT}px)`, 76 marginTop: HEADER_HEIGHT, 77 backgroundColor: theme.background.default, 78 '[data-expo-theme="dark"] &': { 79 backgroundColor: theme.background.screen, 80 }, 81 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 82 // Ditch inner scroll on mobile, which results in weird bugs 83 maxHeight: 'none', 84 }, 85}); 86 87const headerStyle = css({ 88 position: 'fixed', 89 top: 0, 90 width: '100%', 91 height: HEADER_HEIGHT, 92 zIndex: 100, 93}); 94 95const navigationStyle = css({ 96 flexBasis: 256, 97 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 98 display: 'none', 99 }, 100}); 101 102const innerContentStyle = css({ 103 margin: '0 auto', 104 minHeight: `calc(100vh - ${HEADER_HEIGHT}px)`, 105 maxWidth: breakpoints.large, 106 padding: `${spacing[8]}px ${spacing[4]}px`, 107}); 108 109const asideStyle = css({ 110 flexBasis: 288, 111 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 112 display: 'none', 113 }, 114}); 115