xref: /expo/docs/ui/components/Header/Header.tsx (revision e330c216)
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 { 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.small} />
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        </div>
64      )}
65    </>
66  );
67};
68
69const containerStyle = css`
70  display: flex;
71  align-items: center;
72  justify-content: flex-end;
73  position: relative;
74  background-color: ${theme.background.default};
75  z-index: 2;
76  margin: 0 auto;
77  padding: 0 ${spacing[4]}px;
78  height: 60px;
79  box-sizing: border-box;
80  border-bottom: 1px solid ${theme.border.default};
81  gap: ${spacing[4]}px;
82`;
83
84const columnStyle = css`
85  flex-shrink: 0;
86  display: flex;
87  background-color: transparent;
88`;
89
90const leftColumnStyle = css`
91  flex-grow: 1;
92  align-items: center;
93
94  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
95    flex-basis: auto;
96    width: auto;
97  }
98`;
99
100const rightColumnStyle = css`
101  justify-content: flex-end;
102
103  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
104    flex-basis: auto;
105    width: auto;
106  }
107`;
108
109const showOnMobileStyle = css`
110  display: none;
111
112  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
113    display: flex;
114  }
115`;
116
117const hideOnMobileStyle = css`
118  @media screen and (max-width: ${(breakpoints.medium + breakpoints.large) / 2}px) {
119    display: none;
120  }
121`;
122
123const mobileButtonStyle = css`
124  padding: 0 ${spacing[3]}px;
125  margin-left: ${spacing[2]}px;
126
127  &:hover {
128    background-color: ${theme.background.tertiary};
129    box-shadow: none;
130  }
131`;
132
133const mobileButtonActiveStyle = css`
134  background-color: ${theme.background.secondary};
135`;
136
137const mobileSidebarStyle = css`
138  background-color: ${theme.background.secondary};
139  height: calc(100vh - (60px * 2));
140  overflow-y: auto;
141  overflow-x: hidden;
142`;
143