xref: /expo/docs/ui/components/Header/Header.tsx (revision 20924d47)
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