Refactor buttons.

This commit is contained in:
Mike Cao 2020-08-07 00:24:01 -07:00
parent 000f84df96
commit 30b87bc4c4
16 changed files with 175 additions and 63 deletions

1
assets/plus.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M368 224H224V80c0-8.84-7.16-16-16-16h-32c-8.84 0-16 7.16-16 16v144H16c-8.84 0-16 7.16-16 16v32c0 8.84 7.16 16 16 16h144v144c0 8.84 7.16 16 16 16h32c8.84 0 16-7.16 16-16V288h144c8.84 0 16-7.16 16-16v-32c0-8.84-7.16-16-16-16z"/></svg>

After

Width:  |  Height:  |  Size: 303 B

1
assets/trash.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M432 80h-82.4l-34-56.7A48 48 0 0 0 274.4 0H173.6a48 48 0 0 0-41.2 23.3L98.4 80H16A16 16 0 0 0 0 96v16a16 16 0 0 0 16 16h16l21.2 339a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128h16a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16zM173.6 48h100.8l19.2 32H154.4zm173.3 416H101.11l-21-336h287.8z"/></svg>

After

Width:  |  Height:  |  Size: 370 B

View File

@ -3,10 +3,24 @@ import classNames from 'classnames';
import Icon from './Icon';
import styles from './Button.module.css';
export default function Button({ icon, type = 'button', children, className, onClick = () => {} }) {
export default function Button({
type = 'button',
icon,
size,
children,
className,
onClick = () => {},
}) {
return (
<button type={type} className={classNames(styles.button, className)} onClick={onClick}>
{icon && <Icon icon={icon} />}
<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

@ -14,3 +14,15 @@
.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

@ -17,4 +17,5 @@ export const FormError = ({ name }) => (
export const FormRow = ({ children }) => <div className={styles.row}>{children}</div>;
export const FormMessage = ({ children }) => <div className={styles.message}>{children}</div>;
export const FormMessage = ({ children }) =>
children ? <div className={styles.message}>{children}</div> : null;

View File

@ -5,6 +5,10 @@
vertical-align: middle;
}
.icon + * {
margin-left: 10px;
}
.icon svg {
fill: currentColor;
}

View File

@ -56,7 +56,9 @@ export default function Login() {
<FormError name="password" />
</FormRow>
<FormButtons>
<Button type="submit">Login</Button>
<Button className={styles.button} type="submit">
Login
</Button>
</FormButtons>
<FormMessage>{message}</FormMessage>
</Form>

View File

@ -2,3 +2,12 @@
font-size: var(--font-size-xlarge);
text-align: center;
}
.button {
color: var(--gray50);
background: var(--gray900);
}
.button:hover {
background: var(--gray800);
}

12
components/PageHeader.js Normal file
View File

@ -0,0 +1,12 @@
import React, { Children } from 'react';
import styles from './PageHeader.module.css';
export default function PageHeader({ children }) {
const [firstChild, ...otherChildren] = Children.toArray(children);
return (
<div className={styles.header}>
<div className={styles.title}> {firstChild}</div>
{otherChildren && <div>{otherChildren}</div>}
</div>
);
}

View File

@ -0,0 +1,10 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
line-height: 80px;
}
.title {
font-size: var(--font-size-large);
}

View File

@ -1,16 +1,37 @@
import React, { useState, useEffect } from 'react';
import Page from './Page';
import Table from './Table';
import Button from './Button';
import Icon from './Icon';
import PageHeader from './PageHeader';
import Pen from 'assets/pen.svg';
import Trash from 'assets/trash.svg';
import Plus from 'assets/plus.svg';
import { get } from 'lib/web';
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'domain', label: 'Domain' },
];
export default function Settings() {
const [data, setData] = useState();
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'domain', label: 'Domain' },
{
key: 'action',
label: '',
style: { flex: 0 },
render: ({ website_id }) => (
<>
<Button icon={<Pen />} size="S">
<div>Edit</div>
</Button>
<Button icon={<Trash />} size="S">
<div>Delete</div>
</Button>
</>
),
},
];
async function loadData() {
setData(await get(`/api/website`));
}
@ -25,7 +46,13 @@ export default function Settings() {
return (
<Page>
<h2>Settings</h2>
<PageHeader>
<div>Settings</div>
<Button size="S">
<Icon icon={<Plus />} size="S" />
<div>Add website</div>
</Button>
</PageHeader>
<Table columns={columns} rows={data.websites} />
</Page>
);

View File

@ -1,25 +1,32 @@
import React from 'react';
import classNames from 'classnames';
import styles from './Table.module.css';
export default function Table({ columns, rows }) {
return (
<table className={styles.table}>
<thead>
<tr>
{columns.map(({ key, label }) => (
<th key={key}>{label}</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, rowIndex) => (
<tr key={rowIndex}>
{columns.map(({ key }) => (
<td key={`${rowIndex}${key}`}>{row[key]}</td>
))}
</tr>
<div className={styles.table}>
<div className={styles.header}>
{columns.map(({ key, label }) => (
<div key={key} className={styles.head}>
{label}
</div>
))}
</tbody>
</table>
</div>
<div className={styles.body}>
{rows.map((row, rowIndex) => (
<div className={styles.row} key={rowIndex}>
{columns.map(({ key, render, className, style }) => (
<div
key={`${rowIndex}${key}`}
className={classNames(styles.cell, className)}
style={style}
>
{render ? render(row) : row[key]}
</div>
))}
</div>
))}
</div>
</div>
);
}

View File

@ -1,7 +1,27 @@
.table {
width: 100%;
display: flex;
flex-direction: column;
}
.table th {
text-align: left;
.header {
display: flex;
}
.head {
font-size: var(--font-size-small);
font-weight: 600;
line-height: 40px;
flex: 1;
}
.row {
display: flex;
border-bottom: 1px solid var(--gray300);
padding: 10px 0;
}
.cell {
display: flex;
align-items: flex-start;
flex: 1;
}

View File

@ -8,6 +8,7 @@ import { getDateRange } from 'lib/date';
import { get } from 'lib/web';
import { browserFilter, urlFilter, refFilter, deviceFilter, countryFilter } from 'lib/filters';
import styles from './WebsiteDetails.module.css';
import PageHeader from './PageHeader';
const pageviewClasses = 'col-md-12 col-lg-6';
const sessionClasses = 'col-12 col-lg-4';
@ -45,7 +46,7 @@ export default function WebsiteDetails({ websiteId, defaultDateRange = '7day' })
<Page>
<div className="row">
<div className={classNames(styles.chart, 'col')}>
<h2>{data.name}</h2>
<PageHeader>{data.name}</PageHeader>
<WebsiteChart
websiteId={websiteId}
onDataLoad={handleDataLoad}

View File

@ -1,14 +1,18 @@
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { get } from 'lib/web';
import Link from './Link';
import WebsiteChart from './WebsiteChart';
import Page from './Page';
import Icon from './Icon';
import { get } from 'lib/web';
import Button from './Button';
import PageHeader from './PageHeader';
import Arrow from 'assets/arrow-right.svg';
import styles from './WebsiteList.module.css';
export default function WebsiteList() {
const [data, setData] = useState();
const router = useRouter();
async function loadData() {
setData(await get(`/api/website`));
@ -23,24 +27,26 @@ export default function WebsiteList() {
{data &&
data.websites.map(({ website_id, name }) => (
<div key={website_id} className={styles.website}>
<div className={styles.header}>
<h2>
<Link
href="/website/[...id]"
as={`/website/${website_id}/${name}`}
className={styles.title}
>
{name}
</Link>
</h2>
<PageHeader>
<Link
href="/website/[...id]"
as={`/website/${website_id}/${name}`}
className={styles.details}
className={styles.title}
>
<Icon icon={<Arrow />} /> View details
{name}
</Link>
</div>
<Button
icon={<Arrow />}
onClick={() =>
router.push('/website/[...id]', `/website/${website_id}/${name}`, {
shallow: true,
})
}
size="S"
>
<div>View details</div>
</Button>
</PageHeader>
<WebsiteChart key={website_id} title={name} websiteId={website_id} />
</div>
))}

View File

@ -9,21 +9,6 @@
margin-bottom: 0;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
color: var(--gray900) !important;
}
.details {
font-size: 14px;
font-weight: 600;
}
.details svg {
margin-right: 5px;
.button {
font-size: var(--font-size-small);
}