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