mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-22 09:57:00 +01:00
Refactor buttons.
This commit is contained in:
parent
000f84df96
commit
30b87bc4c4
1
assets/plus.svg
Normal file
1
assets/plus.svg
Normal 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
1
assets/trash.svg
Normal 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 |
@ -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>
|
||||
);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -5,6 +5,10 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon + * {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.icon svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
12
components/PageHeader.js
Normal 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>
|
||||
);
|
||||
}
|
10
components/PageHeader.module.css
Normal file
10
components/PageHeader.module.css
Normal 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);
|
||||
}
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
))}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user