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