Skip to content

Commit c6035aa

Browse files
committed
Initial nav menu + user dropdown
1 parent 4183a4e commit c6035aa

File tree

6 files changed

+256
-9
lines changed

6 files changed

+256
-9
lines changed

site/components/Icons/Logout.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import SvgIcon, { SvgIconProps } from "@material-ui/core/SvgIcon"
2+
import React from "react"
3+
4+
export const LogoutIcon = (props: SvgIconProps): JSX.Element => (
5+
<SvgIcon {...props} viewBox="0 0 20 20">
6+
<path
7+
fillRule="evenodd"
8+
clipRule="evenodd"
9+
d="M3.92523 18.5071H11.2169C11.8878 18.5063 12.4314 17.9626 12.4322 17.2918V15.4689H11.2169V17.2918H3.92523V2.70844H11.2169V4.53136H12.4322V2.70844V2.70845C12.4314 2.03759 11.8878 1.49394 11.2169 1.49316H3.92524C3.25438 1.49393 2.71073 2.03758 2.70996 2.70844V17.2918V17.2918C2.71073 17.9626 3.25438 18.5063 3.92523 18.5071Z"
10+
fill="currentColor"
11+
/>
12+
<path
13+
fillRule="evenodd"
14+
clipRule="evenodd"
15+
d="M12.6751 17.292C12.6742 18.0968 12.022 18.7491 11.2171 18.75H3.92513V18.507L11.2168 18.5072C11.8877 18.5064 12.4313 17.9625 12.4321 17.2917V15.4688H11.2168V17.2917H3.92513V2.70834H11.2168V4.53125H12.4321V2.70848C12.4313 2.03762 11.8877 1.49383 11.2168 1.49306H3.92513V1.25H11.2168C12.0217 1.25093 12.6742 1.90319 12.6751 2.70806V4.77431H10.9737V2.95139H4.16818V17.0486H10.9737V15.2257H12.6751V17.292ZM2.70985 2.70833C2.71062 2.03747 3.25427 1.49383 3.92513 1.49306V1.25C3.12025 1.25092 2.46772 1.90318 2.4668 2.70805V17.2917C2.46772 18.0965 3.12025 18.7491 3.92513 18.75V18.507C3.25427 18.5062 2.71062 17.9624 2.70985 17.2915V2.70833Z"
16+
fill="currentColor"
17+
/>
18+
<path
19+
fillRule="evenodd"
20+
clipRule="evenodd"
21+
d="M12.7879 12.7867L14.9669 10.6077H6.35547V9.39244H14.9669L12.7879 7.21345L13.6471 6.35425L17.293 10.0001L13.6471 13.6459L12.7879 12.7867Z"
22+
fill="currentColor"
23+
/>
24+
<path
25+
fillRule="evenodd"
26+
clipRule="evenodd"
27+
d="M12.4446 12.7867L14.3805 10.8508H6.11279V9.14937H14.3805L12.4446 7.21343L13.6475 6.0105L17.6371 10.0001L13.6475 13.9896L12.4446 12.7867ZM14.9673 9.39243H6.35585V10.6077H14.9673L12.7883 12.7867L13.6475 13.6459L17.2934 10.0001L13.6475 6.35423L12.7883 7.21343L14.9673 9.39243Z"
28+
fill="currentColor"
29+
/>
30+
</SvgIcon>
31+
)

site/components/Icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { CoderIcon } from "./CoderIcon"
22
export { Logo } from "./Logo"
3+
export * from "./Logout"
34
export { WorkspacesIcon } from "./WorkspacesIcon"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Popover, { PopoverProps } from "@material-ui/core/Popover"
2+
import { fade, makeStyles } from "@material-ui/core/styles"
3+
import React from "react"
4+
5+
type BorderedMenuVariant = "manage-dropdown" | "user-dropdown"
6+
7+
type BorderedMenuProps = Omit<PopoverProps, "variant"> & {
8+
variant?: BorderedMenuVariant
9+
}
10+
11+
export const BorderedMenu: React.FC<BorderedMenuProps> = ({ children, variant, ...rest }) => {
12+
const styles = useStyles()
13+
14+
return (
15+
<Popover classes={{ root: styles.root, paper: styles.paperRoot }} data-variant={variant} {...rest}>
16+
{children}
17+
</Popover>
18+
)
19+
}
20+
21+
const useStyles = makeStyles((theme) => ({
22+
root: {
23+
paddingBottom: theme.spacing(1),
24+
},
25+
paperRoot: {
26+
width: "292px",
27+
border: `2px solid ${theme.palette.primary.main}`,
28+
borderRadius: 7,
29+
boxShadow: `4px 4px 0px ${fade(theme.palette.primary.main, 0.2)}`,
30+
},
31+
}))
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import Avatar from "@material-ui/core/Avatar"
2+
import Badge from "@material-ui/core/Badge"
3+
import ListItemIcon from "@material-ui/core/ListItemIcon"
4+
import ListItemText from "@material-ui/core/ListItemText"
5+
import MenuItem from "@material-ui/core/MenuItem"
6+
import { fade, makeStyles } from "@material-ui/core/styles"
7+
//import AccountIcon from "@material-ui/icons/AccountCircleOutlined"
8+
import KeyboardArrowDown from "@material-ui/icons/KeyboardArrowDown"
9+
import KeyboardArrowUp from "@material-ui/icons/KeyboardArrowUp"
10+
import React, { useState } from "react"
11+
import { LogoutIcon } from "../Icons"
12+
import { BorderedMenu } from "./BorderedMenu"
13+
import { UserProfileCard } from "../User/UserProfileCard"
14+
15+
import { User } from "../../contexts/UserContext"
16+
import Divider from "@material-ui/core/Divider"
17+
18+
const navHeight = 56
19+
20+
export interface UserDropdownProps {
21+
user: User
22+
onSignOut: () => void
23+
}
24+
25+
export const UserDropdown: React.FC<UserDropdownProps> = ({ user, onSignOut }: UserDropdownProps) => {
26+
const styles = useStyles()
27+
28+
const [anchorEl, setAnchorEl] = useState<HTMLElement | undefined>()
29+
const handleDropdownClick = (ev: React.MouseEvent<HTMLLIElement>): void => {
30+
setAnchorEl(ev.currentTarget)
31+
}
32+
const onPopoverClose = () => {
33+
setAnchorEl(undefined)
34+
}
35+
36+
// TODO: what does this do?
37+
const isSelected = false
38+
39+
return (
40+
<>
41+
<div>
42+
<MenuItem onClick={handleDropdownClick} selected={isSelected}>
43+
<div className={styles.inner}>
44+
{user && (
45+
<Badge overlap="circle">
46+
<Avatar>T</Avatar>
47+
</Badge>
48+
)}
49+
{anchorEl ? (
50+
<KeyboardArrowUp className={`${styles.arrowIcon} ${styles.arrowIconUp}`} />
51+
) : (
52+
<KeyboardArrowDown className={styles.arrowIcon} />
53+
)}
54+
</div>
55+
</MenuItem>
56+
</div>
57+
58+
<BorderedMenu
59+
anchorEl={anchorEl}
60+
getContentAnchorEl={null}
61+
open={!!anchorEl}
62+
anchorOrigin={{
63+
vertical: "bottom",
64+
horizontal: "right",
65+
}}
66+
transformOrigin={{
67+
vertical: "top",
68+
horizontal: "right",
69+
}}
70+
marginThreshold={0}
71+
variant="user-dropdown"
72+
onClose={onPopoverClose}
73+
>
74+
{user && (
75+
<div className={styles.userInfo}>
76+
<UserProfileCard user={user} onAvatarClick={onPopoverClose} />
77+
78+
<Divider className={styles.divider} />
79+
80+
<MenuItem className={styles.menuItem} onClick={onSignOut}>
81+
<ListItemIcon className={styles.icon}>
82+
<LogoutIcon />
83+
</ListItemIcon>
84+
<ListItemText primary="Sign Out" />
85+
</MenuItem>
86+
</div>
87+
)}
88+
</BorderedMenu>
89+
</>
90+
)
91+
}
92+
93+
export const useStyles = makeStyles((theme) => ({
94+
divider: {
95+
marginTop: theme.spacing(1),
96+
marginBottom: theme.spacing(1),
97+
},
98+
inner: {
99+
display: "flex",
100+
alignItems: "center",
101+
minWidth: 0,
102+
maxWidth: 300,
103+
},
104+
105+
userInfo: {
106+
marginBottom: theme.spacing(1),
107+
},
108+
arrowIcon: {
109+
color: fade(theme.palette.primary.contrastText, 0.7),
110+
marginLeft: theme.spacing(1),
111+
width: 16,
112+
height: 16,
113+
},
114+
arrowIconUp: {
115+
color: theme.palette.primary.contrastText,
116+
},
117+
118+
menuItem: {
119+
height: 44,
120+
padding: `${theme.spacing(1.5)}px ${theme.spacing(2.75)}px`,
121+
122+
"&:hover": {
123+
backgroundColor: fade(theme.palette.primary.light, 0.1),
124+
transition: "background-color 0.3s ease",
125+
},
126+
},
127+
128+
icon: {
129+
color: theme.palette.text.secondary,
130+
},
131+
}))

site/components/Navbar/index.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import Link from "next/link"
77

88
import { User } from "../../contexts/UserContext"
99
import { Logo } from "../Icons"
10+
import { UserDropdown } from "./UserDropdown"
1011

1112
export interface NavbarProps {
1213
user?: User
1314
}
1415

15-
export const Navbar: React.FC<NavbarProps> = () => {
16+
export const Navbar: React.FC<NavbarProps> = ({ user }) => {
1617
const styles = useStyles()
1718
return (
1819
<div className={styles.root}>
@@ -23,14 +24,8 @@ export const Navbar: React.FC<NavbarProps> = () => {
2324
</Button>
2425
</Link>
2526
</div>
26-
<div className={styles.fullWidth}>
27-
<div className={styles.title}>Coder v2</div>
28-
</div>
29-
<div className={styles.fixed}>
30-
<List>
31-
<ListSubheader>Manage</ListSubheader>
32-
</List>
33-
</div>
27+
<div className={styles.fullWidth} />
28+
<div className={styles.fixed}>{user && <UserDropdown user={user} onSignOut={() => alert("sign out")} />}</div>
3429
</div>
3530
)
3631
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Avatar from "@material-ui/core/Avatar"
2+
import { makeStyles } from "@material-ui/core/styles"
3+
import Typography from "@material-ui/core/Typography"
4+
import React from "react"
5+
6+
import { User } from "../../contexts/UserContext"
7+
8+
interface UserProfileCardProps {
9+
user: User
10+
}
11+
12+
export const UserProfileCard: React.FC<UserProfileCardProps> = ({ user }) => {
13+
const styles = useStyles()
14+
15+
return (
16+
<div className={styles.root}>
17+
<div className={styles.avatarContainer}>
18+
<Avatar className={styles.avatar}>T</Avatar>
19+
</div>
20+
<Typography className={styles.userName}>{user.username}</Typography>
21+
<Typography className={styles.userEmail}>{user.email}</Typography>
22+
</div>
23+
)
24+
}
25+
26+
const useStyles = makeStyles((theme) => ({
27+
root: {
28+
paddingTop: theme.spacing(3),
29+
textAlign: "center",
30+
},
31+
avatarContainer: {
32+
width: "100%",
33+
display: "flex",
34+
alignItems: "center",
35+
justifyContent: "center",
36+
},
37+
avatar: {
38+
width: 48,
39+
height: 48,
40+
borderRadius: "50%",
41+
marginBottom: theme.spacing(1),
42+
transition: `transform .2s`,
43+
44+
"&:hover": {
45+
transform: `scale(1.1)`,
46+
},
47+
},
48+
userName: {
49+
fontSize: 16,
50+
marginBottom: theme.spacing(0.5),
51+
},
52+
userEmail: {
53+
fontSize: 14,
54+
letterSpacing: 0.2,
55+
color: theme.palette.text.secondary,
56+
marginBottom: theme.spacing(1.5),
57+
},
58+
}))

0 commit comments

Comments
 (0)