Website edit functionality.

This commit is contained in:
Mike Cao 2020-08-07 02:27:12 -07:00
parent 30b87bc4c4
commit 00e232fee8
63 changed files with 301 additions and 94 deletions

View file

@ -0,0 +1,27 @@
import React from 'react';
import classNames from 'classnames';
import Icon from './Icon';
import styles from './Button.module.css';
export default function Button({
type = 'button',
icon,
size,
children,
className,
onClick = () => {},
}) {
return (
<button
type={type}
className={classNames(styles.button, className, {
[styles.small]: size === 'S',
[styles.large]: size === 'L',
})}
onClick={onClick}
>
{icon && <Icon icon={icon} size={size} />}
{children}
</button>
);
}

View file

@ -0,0 +1,28 @@
.button {
display: flex;
justify-content: center;
align-items: center;
font-size: var(--font-size-normal);
background: var(--gray100);
padding: 8px 16px;
border-radius: 4px;
border: 0;
outline: none;
cursor: pointer;
}
.button:hover {
background: #eaeaea;
}
.button + .button {
margin-left: 10px;
}
.small {
font-size: var(--font-size-small);
}
.large {
font-size: var(--font-size-large);
}

View file

@ -0,0 +1,18 @@
import React from 'react';
import classNames from 'classnames';
import styles from './Icon.module.css';
export default function Icon({ icon, className, size = 'M' }) {
return (
<div
className={classNames(styles.icon, className, {
[styles.xl]: size === 'XL',
[styles.large]: size === 'L',
[styles.medium]: size === 'M',
[styles.small]: size === 'S',
})}
>
{icon}
</div>
);
}

View file

@ -0,0 +1,34 @@
.icon {
display: inline-flex;
justify-content: center;
align-items: center;
vertical-align: middle;
}
.icon + * {
margin-left: 10px;
}
.icon svg {
fill: currentColor;
}
.xl > svg {
width: 48px;
height: 48px;
}
.large > svg {
width: 24px;
height: 24px;
}
.medium > svg {
width: 16px;
height: 16px;
}
.small > svg {
width: 12px;
height: 12px;
}

View file

@ -0,0 +1,12 @@
import React from 'react';
import classNames from 'classnames';
import NextLink from 'next/link';
import styles from './Link.module.css';
export default function Link({ className, children, ...props }) {
return (
<NextLink {...props}>
<a className={classNames(styles.link, className)}>{children}</a>
</NextLink>
);
}

View file

@ -0,0 +1,23 @@
.link,
.link:active,
.link:visited {
position: relative;
color: #2c2c2c;
text-decoration: none;
}
.link:before {
content: '';
position: absolute;
bottom: -2px;
width: 0;
height: 2px;
background: #2680eb;
opacity: 0.5;
transition: width 100ms;
}
.link:hover:before {
width: 100%;
transition: width 100ms;
}

View file

@ -0,0 +1,24 @@
import React from 'react';
import classNames from 'classnames';
import styles from './Menu.module.css';
export default function Menu({ options = [], className, align = 'left', onSelect = () => {} }) {
return (
<div
className={classNames(styles.menu, className, {
[styles.left]: align === 'left',
[styles.right]: align === 'right',
})}
>
{options.map(({ label, value, className: optionClassName }) => (
<div
key={value}
className={classNames(styles.option, optionClassName)}
onClick={e => onSelect(value, e)}
>
{label}
</div>
))}
</div>
);
}

View file

@ -0,0 +1,31 @@
.menu {
position: absolute;
min-width: 100px;
top: 100%;
margin-top: 4px;
border: 1px solid var(--gray500);
border-radius: 4px;
overflow: hidden;
z-index: 2;
}
.option {
font-size: var(--font-size-small);
font-weight: normal;
background: #fff;
padding: 4px 16px;
cursor: pointer;
white-space: nowrap;
}
.option:hover {
background: #f5f5f5;
}
.left {
left: 0;
}
.right {
right: 0;
}

View file

@ -0,0 +1,56 @@
import React, { useState, useRef } from 'react';
import { useSelector } from 'react-redux';
import { useRouter } from 'next/router';
import Menu from './Menu';
import Icon from './Icon';
import useDocumentClick from 'hooks/useDocumentClick';
import User from 'assets/user.svg';
import Chevron from 'assets/chevron-down.svg';
import styles from './UserButton.module.css';
export default function UserButton() {
const [showMenu, setShowMenu] = useState(false);
const user = useSelector(state => state.user);
const ref = useRef();
const router = useRouter();
const menuOptions = [
{
label: (
<>
Logged in as <b>{user.username}</b>
</>
),
value: 'username',
className: styles.username,
},
{ label: 'Account', value: 'account' },
{ label: 'Logout', value: 'logout' },
];
function handleSelect(value) {
setShowMenu(false);
if (value === 'account') {
router.push('/account');
} else if (value === 'logout') {
router.push('/logout');
}
}
useDocumentClick(e => {
if (!ref.current.contains(e.target)) {
setShowMenu(false);
}
});
return (
<div ref={ref} className={styles.container}>
<div onClick={() => setShowMenu(state => !state)}>
<Icon icon={<User />} size="L" className={styles.icon} />
<Icon icon={<Chevron />} size="S" />
</div>
{showMenu && <Menu options={menuOptions} onSelect={handleSelect} align="right" />}
</div>
);
}

View file

@ -0,0 +1,17 @@
.container {
display: flex;
position: relative;
cursor: pointer;
}
.icon {
margin-right: 8px;
}
.username {
border-bottom: 1px solid var(--gray500);
}
.username:hover {
background: var(--gray50);
}