xref: /expo/docs/ui/components/Header/Header.tsx (revision bb5069cd)
1import { css } from '@emotion/react';
2import { theme, breakpoints, HamburgerIcon, iconSize, spacing } from '@expo/styleguide';
3import React from 'react';
4
5import { Search } from '../Search';
6import { Logo } from './Logo';
7import { ThemeSelector } from './ThemeSelector';
8
9import { Button } from '~/ui/components/Button';
10import { SidebarFooter, SidebarHead } from '~/ui/components/Sidebar';
11import { BOLD } from '~/ui/components/Text';
12
13type HeaderProps = {
14  sidebar: React.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  return (
27    <>
28      <nav css={[containerStyle, isMobileMenuVisible]}>
29        <div css={[columnStyle, leftColumnStyle]}>
30          <Logo />
31        </div>
32        <div css={[columnStyle, rightColumnStyle]}>
33          <Search />
34          <div css={hideOnMobileStyle}>
35            <ThemeSelector />
36          </div>
37          <div css={showOnMobileStyle}>
38            <Button
39              theme="transparent"
40              css={[mobileButtonStyle, isMobileMenuVisible && mobileButtonActiveStyle]}
41              onClick={() => {
42                setMobileMenuVisible(!isMobileMenuVisible);
43              }}>
44              <HamburgerIcon size={iconSize.sm} />
45            </Button>
46          </div>
47        </div>
48      </nav>
49      {isMobileMenuVisible && (
50        <nav css={[containerStyle, showOnMobileStyle]}>
51          <div css={[columnStyle, leftColumnStyle]}>
52            <BOLD>Theme</BOLD>
53          </div>
54          <div css={[columnStyle, rightColumnStyle]}>
55            <ThemeSelector />
56          </div>
57        </nav>
58      )}
59      {isMobileMenuVisible && (
60        <div css={mobileSidebarStyle}>
61          <SidebarHead sidebarActiveGroup={sidebarActiveGroup} />
62          {sidebar}
63          <SidebarFooter />
64        </div>
65      )}
66    </>
67  );
68};
69
70const containerStyle = css`
71  display: flex;
72  align-items: center;
73  justify-content: flex-end;
74  position: relative;
75  background-color: ${theme.background.default};
76  z-index: 2;
77  margin: 0 auto;
78  padding: 0 ${spacing[4]}px;
79  height: 60px;
80  box-sizing: border-box;
81  border-bottom: 1px solid ${theme.border.default};
82  gap: ${spacing[4]}px;
83`;
84
85const columnStyle = css`
86  flex-shrink: 0;
87  display: flex;
88  background-color: transparent;
89`;
90
91const leftColumnStyle = css`
92  flex-grow: 1;
93  align-items: center;
94
95  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
96    flex-basis: auto;
97    width: auto;
98  }
99`;
100
101const rightColumnStyle = css`
102  justify-content: flex-end;
103
104  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
105    flex-basis: auto;
106    width: auto;
107  }
108`;
109
110const showOnMobileStyle = css`
111  display: none;
112
113  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
114    display: flex;
115  }
116`;
117
118const hideOnMobileStyle = css`
119  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
120    display: none;
121  }
122`;
123
124const mobileButtonStyle = css`
125  padding: 0 ${spacing[3]}px;
126  margin-left: ${spacing[2]}px;
127
128  &:hover {
129    background-color: ${theme.background.element};
130    box-shadow: none;
131  }
132`;
133
134const mobileButtonActiveStyle = css`
135  background-color: ${theme.background.subtle};
136`;
137
138const mobileSidebarStyle = css`
139  background-color: ${theme.background.subtle};
140  height: calc(100vh - (60px * 2));
141  overflow-y: auto;
142  overflow-x: hidden;
143`;
144