1import { css } from '@emotion/react'; 2import { theme, breakpoints, HamburgerIcon, iconSize, spacing, GithubIcon } from '@expo/styleguide'; 3import { ReactNode } from 'react'; 4 5import { Logo } from './Logo'; 6import { ThemeSelector } from './ThemeSelector'; 7 8import { Button } from '~/ui/components/Button'; 9import { SidebarFooter, SidebarHead } from '~/ui/components/Sidebar'; 10import { A, BOLD } from '~/ui/components/Text'; 11 12type HeaderProps = { 13 sidebar: ReactNode; 14 sidebarActiveGroup: string; 15 isMobileMenuVisible: boolean; 16 setMobileMenuVisible: (isMobileMenuVisible: boolean) => void; 17}; 18 19export const Header = ({ 20 sidebar, 21 sidebarActiveGroup, 22 isMobileMenuVisible, 23 setMobileMenuVisible, 24}: HeaderProps) => { 25 const isArchive = sidebarActiveGroup === 'archive'; 26 return ( 27 <> 28 <nav css={[containerStyle, isMobileMenuVisible]}> 29 <div css={[columnStyle, leftColumnStyle]}> 30 <Logo subgroup={isArchive ? 'Archive' : undefined} /> 31 </div> 32 <div css={[columnStyle, rightColumnStyle]}> 33 <A isStyled css={headerLink} href="https://blog.expo.dev"> 34 Blog 35 </A> 36 <A href="https://github.com/expo/expo" ariaLabel="GitHub"> 37 <GithubIcon title="" /> 38 </A> 39 <div css={hideOnMobileStyle}> 40 <ThemeSelector /> 41 </div> 42 <div css={showOnMobileStyle}> 43 <Button 44 theme="transparent" 45 css={[mobileButtonStyle, isMobileMenuVisible && mobileButtonActiveStyle]} 46 onClick={() => { 47 setMobileMenuVisible(!isMobileMenuVisible); 48 }}> 49 <HamburgerIcon size={iconSize.sm} /> 50 </Button> 51 </div> 52 </div> 53 </nav> 54 {isMobileMenuVisible && ( 55 <nav css={[containerStyle, showOnMobileStyle]}> 56 <div css={[columnStyle, leftColumnStyle]}> 57 <BOLD>Theme</BOLD> 58 </div> 59 <div css={[columnStyle, rightColumnStyle]}> 60 <ThemeSelector /> 61 </div> 62 </nav> 63 )} 64 {isMobileMenuVisible && ( 65 <div css={mobileSidebarStyle}> 66 <SidebarHead sidebarActiveGroup={sidebarActiveGroup} /> 67 {sidebar} 68 <SidebarFooter /> 69 </div> 70 )} 71 </> 72 ); 73}; 74 75const containerStyle = css` 76 display: flex; 77 align-items: center; 78 justify-content: flex-end; 79 position: relative; 80 background-color: ${theme.background.default}; 81 z-index: 2; 82 margin: 0 auto; 83 padding: 0 ${spacing[4]}px; 84 height: 60px; 85 box-sizing: border-box; 86 border-bottom: 1px solid ${theme.border.default}; 87 gap: ${spacing[4]}px; 88`; 89 90const columnStyle = css` 91 flex-shrink: 0; 92 display: flex; 93 gap: ${spacing[8]}px; 94 align-items: center; 95 background-color: transparent; 96 97 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 98 gap: ${spacing[4]}px; 99 } 100`; 101 102const headerLink = css` 103 color: ${theme.text.secondary}; 104 105 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 106 margin-right: ${spacing[2]}px; 107 } 108`; 109 110const leftColumnStyle = css` 111 flex-grow: 1; 112 align-items: center; 113 114 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 115 flex-basis: auto; 116 width: auto; 117 } 118`; 119 120const rightColumnStyle = css` 121 justify-content: flex-end; 122 123 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 124 flex-basis: auto; 125 width: auto; 126 } 127`; 128 129const showOnMobileStyle = css` 130 display: none; 131 132 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 133 display: flex; 134 } 135`; 136 137const hideOnMobileStyle = css` 138 @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) { 139 display: none; 140 } 141`; 142 143const mobileButtonStyle = css` 144 padding: 0 ${spacing[3]}px; 145 146 &:hover { 147 background-color: ${theme.background.element}; 148 box-shadow: none; 149 } 150`; 151 152const mobileButtonActiveStyle = css` 153 background-color: ${theme.background.subtle}; 154`; 155 156const mobileSidebarStyle = css` 157 background-color: ${theme.background.subtle}; 158 height: calc(100vh - (60px * 2)); 159 overflow-y: auto; 160 overflow-x: hidden; 161`; 162