1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-11-25 06:24:19 +00:00

🔧 Refactor summary in multiple components

This commit is contained in:
Steffo 2021-05-18 17:51:36 +02:00
parent d38caffb0a
commit f6bf948742
Signed by: steffo
GPG key ID: 6965406171929D01
20 changed files with 301 additions and 259 deletions

View file

@ -8,6 +8,8 @@ import make_icon from "../../utils/make_icon"
* A clickable button.
*
* @param children - The contents of the button.
* @param disabled - Whether the button is disabled or not.
* @param onClick - Function to call when the button is clicked. Won't be called if the button is disabled.
* @param className - Additional class(es) that should be added to the button.
* @param color - The color of the button. Either `Red`, `Grey`, `Green` or `Yellow`.
* @param icon - The FontAwesome IconDefinition of the icon that should be rendered in the button.
@ -15,9 +17,15 @@ import make_icon from "../../utils/make_icon"
* @returns {JSX.Element}
* @constructor
*/
export default function Button({ children, className, color, icon, ...props }) {
export default function Button({ children, disabled, onClick, className, color, icon, ...props }) {
return (
<button type={"button"} className={classNames(Style.Button, Style[`Button${color}`], className)} {...props}>
<button
type={"button"}
className={classNames(Style.Button, Style[`Button${color}`], disabled ? null : "Clickable", className)}
onClick={disabled ? null : onClick}
disabled={disabled}
{...props}
>
{children} {make_icon(icon, Style.Icon)}
</button>
)

View file

@ -15,14 +15,6 @@
opacity: 0.5;
}
.Button:hover {
filter: brightness(110%);
}
.Button:active {
filter: brightness(125%);
}
.Button:focus-visible {
outline: 4px solid var(--outline);
}

View file

@ -30,7 +30,7 @@ export default function ButtonSidebar({ icon, children, to, className, ...props
return (
<Link to={to} className={Style.ButtonLink}>
<div className={classNames(Style.ButtonSidebar, className)} {...props}>
<div className={classNames(Style.ButtonSidebar, "Clickable", className)} {...props}>
{make_icon(icon, Style.ButtonIcon)}
<div className={Style.ButtonText}>
{children}

View file

@ -1,59 +0,0 @@
import React from "react"
import Style from "./Summary.module.css"
import classNames from "classnames"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
/**
* A long line displaying the summary of a certain entity, such as a repository or an user.
*
* @param icon - The icon of the summary.
* @param title - The title of the summary.
* @param subtitle - The subtitle of the summary.
* @param onClick - A function to call when the summary is clicked.
* @param upperLabel - The label for the upper value.
* @param upperValue - The upper value.
* @param lowerLabel - The label for the lower value.
* @param lowerValue - The lower value.
* @param buttons - Buttons to add to the right of the summary.
* @param className - Additional class(es) to add to the summary.
* @param props - Additional props to pass to the summary.
* @returns {JSX.Element}
* @constructor
*/
export default function Summary(
{ icon, title, subtitle, onClick, upperLabel, upperValue, lowerLabel, lowerValue, buttons, className, ...props },
) {
return (
<div className={classNames(Style.Summary, className)} {...props}>
<div className={classNames(Style.Left, onClick ? Style.Clickable : null)} onClick={onClick}>
<div className={Style.IconContainer}>
<FontAwesomeIcon icon={icon}/>
</div>
<div className={Style.Title}>
{title}
</div>
<div className={Style.Subtitle}>
{subtitle}
</div>
</div>
<div className={Style.Middle}>
<div className={classNames(Style.Label, Style.Upper)}>
{upperLabel}
</div>
<div className={classNames(Style.Value, Style.Upper)}>
{upperValue}
</div>
<div className={classNames(Style.Label, Style.Lower)}>
{lowerLabel}
</div>
<div className={classNames(Style.Value, Style.Lower)}>
{lowerValue}
</div>
</div>
<div className={Style.Right}>
{buttons}
</div>
</div>
)
}

View file

@ -1,110 +0,0 @@
.Summary {
width: 100%;
height: 60px;
margin: 10px 0;
display: flex;
}
.Clickable {
cursor: pointer;
}
.Clickable:hover {
filter: brightness(110%);
}
.Left {
width: 280px;
height: 60px;
display: grid;
grid-template-areas:
"a b"
"a c"
"a d"
"a e";
grid-template-columns: auto 1fr;
grid-template-rows: 5px 1fr 1fr 5px;
background-color: var(--bg-accent);
border-radius: 30px 0 0 30px;
}
.IconContainer {
margin: 5px 15px 5px 5px;
width: 50px;
height: 50px;
border-radius: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--bg-light);
color: var(--fg-primary);
font-size: x-large;
grid-area: a;
}
.Title {
grid-area: c;
align-self: flex-end;
}
.Subtitle {
grid-area: d;
font-size: small;
align-self: flex-start;
}
.Middle {
flex-grow: 1;
height: 60px;
padding: 5px 20px;
background-color: var(--bg-light);
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr;
grid-column-gap: 10px;
align-items: center;
font-size: small;
}
.Middle .Label {
grid-column: 1;
text-align: right;
font-weight: bold;
}
.Middle .Value {
grid-column: 2;
}
.Middle .Upper {
grid-row: 1;
}
.Middle .Lower {
grid-row: 2;
}
.Right {
width: 280px;
height: 60px;
padding: 5px;
background-color: var(--bg-accent);
border-radius: 0 30px 30px 0;
display: flex;
justify-content: flex-end;
align-items: center;
}

View file

@ -0,0 +1,12 @@
import React from "react"
import Style from "./SummaryBase.module.css"
import classNames from "classnames"
export default function SummaryBase({ children, className, ...props }) {
return (
<div className={classNames(Style.SummaryBase, className)} {...props}>
{children}
</div>
)
}

View file

@ -0,0 +1,8 @@
.SummaryBase {
width: 100%;
height: 60px;
margin: 10px 0;
display: flex;
}

View file

@ -0,0 +1,11 @@
import React from "react"
import Style from "./SummaryButton.module.css"
import classNames from "classnames"
import Button from "../Button"
export default function SummaryButton({ className, ...props }) {
return (
<Button className={classNames(Style.SummaryButton, className)} {...props}/>
)
}

View file

@ -0,0 +1,8 @@
.SummaryButton {
width: 90px;
box-shadow: none;
border-radius: 0;
margin: 0;
}

View file

@ -0,0 +1,23 @@
import React from "react"
import Style from "./SummaryLabels.module.css"
import classNames from "classnames"
export default function SummaryLabels({ children, upperLabel, upperValue, lowerLabel, lowerValue, className, ...props }) {
return (
<div className={classNames(Style.SummaryLabels, className)} {...props}>
<div className={classNames(Style.Label, Style.Upper)}>
{upperLabel}
</div>
<div className={classNames(Style.Value, Style.Upper)}>
{upperValue}
</div>
<div className={classNames(Style.Label, Style.Lower)}>
{lowerLabel}
</div>
<div className={classNames(Style.Value, Style.Lower)}>
{lowerValue}
</div>
</div>
)
}

View file

@ -0,0 +1,33 @@
.SummaryLabels {
flex-grow: 3;
padding: 5px 20px;
background-color: var(--bg-light);
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr;
grid-column-gap: 10px;
align-items: center;
font-size: small;
}
.Label {
grid-column: 1;
text-align: right;
font-weight: bold;
}
.Value {
grid-column: 2;
}
.Upper {
grid-row: 1;
}
.Lower {
grid-row: 2;
}

View file

@ -0,0 +1,25 @@
import React from "react"
import Style from "./SummaryLeft.module.css"
import classNames from "classnames"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
export default function SummaryLeft({ icon, title, subtitle, className, onClick, ...props }) {
return (
<div
className={classNames(Style.SummaryLeft, onClick ? "Clickable" : null, className)}
onClick={onClick}
{...props}
>
<div className={Style.IconContainer}>
<FontAwesomeIcon icon={icon}/>
</div>
<div className={Style.Title}>
{title}
</div>
<div className={Style.Subtitle}>
{subtitle}
</div>
</div>
)
}

View file

@ -0,0 +1,46 @@
.SummaryLeft {
width: 275px;
display: grid;
grid-template-areas:
"a b"
"a c"
"a d"
"a e";
grid-template-columns: auto 1fr;
grid-template-rows: 5px 1fr 1fr 5px;
justify-content: stretch;
align-items: stretch;
background-color: var(--bg-accent);
border-radius: 30px 0 0 30px;
}
.IconContainer {
margin: 5px 15px 5px 5px;
width: 50px;
height: 50px;
border-radius: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--bg-light);
color: var(--fg-primary);
font-size: x-large;
grid-area: a;
}
.Title {
grid-area: c;
align-self: flex-end;
}
.Subtitle {
grid-area: d;
font-size: small;
align-self: flex-start;
}

View file

@ -0,0 +1,12 @@
import React from "react"
import Style from "./SummaryRight.module.css"
import classNames from "classnames"
export default function SummaryRight({ children, className, ...props }) {
return (
<div className={classNames(Style.SummaryRight, className)} {...props}>
{children}
</div>
)
}

View file

@ -0,0 +1,7 @@
.SummaryRight {
min-width: 30px;
padding: 5px;
background-color: var(--bg-accent);
border-radius: 0 30px 30px 0;
}

View file

@ -0,0 +1,12 @@
import React from "react"
import Style from "./SummaryText.module.css"
import classNames from "classnames"
export default function SummaryText({ children, className, ...props }) {
return (
<div className={classNames(Style.SummaryText, className)} {...props}>
{children}
</div>
)
}

View file

@ -45,7 +45,7 @@ export default function BoxRepositoriesActive({
deleteSelf={() => destroyRepository(repo["id"])}
canArchive={true}
canEdit={true}
canDelete={repo["owner"]["username"] === user["username"]}
canDelete={false}
running={running}
/>
))

View file

@ -1,9 +1,12 @@
import React, { useContext } from "react"
import Button from "../base/Button"
import { faArchive, faFolder, faFolderOpen, faPencilAlt, faTrash } from "@fortawesome/free-solid-svg-icons"
import { useHistory } from "react-router"
import Summary from "../base/Summary"
import ContextLanguage from "../../contexts/ContextLanguage"
import SummaryBase from "../base/summary/SummaryBase"
import SummaryLeft from "../base/summary/SummaryLeft"
import SummaryLabels from "../base/summary/SummaryLabels"
import SummaryButton from "../base/summary/SummaryButton"
import SummaryRight from "../base/summary/SummaryRight"
/**
@ -36,51 +39,51 @@ export default function SummaryRepository(
history.push(`/repositories/${repo.id}/edit`)
}
const buttons = <>
return (
<SummaryBase>
<SummaryLeft
icon={repo.is_active ? faFolderOpen : faFolder}
title={repo.name}
subtitle={repo.owner ? repo.owner.username : null}
onClick={onRepoClick}
/>
<SummaryLabels
upperLabel={strings.created}
upperValue={repo.start ? new Date(repo.start).toLocaleString() : null}
lowerLabel={strings.archived}
lowerValue={repo.end ? new Date(repo.end).toLocaleString() : null}
/>
{canDelete ?
<Button
<SummaryButton
color={"Red"}
icon={faTrash}
onClick={deleteSelf}
disabled={running}
>
{strings.delete}
</Button>
</SummaryButton>
: null}
{canEdit ?
<Button
<SummaryButton
color={"Yellow"}
icon={faPencilAlt}
onClick={onEditClick}
disabled={running}
>
{strings.edit}
</Button>
</SummaryButton>
: null}
{canArchive ?
<Button
<SummaryButton
color={"Grey"}
icon={faArchive}
onClick={archiveSelf}
disabled={running}
>
{strings.archive}
</Button>
</SummaryButton>
: null}
</>
return (
<Summary
icon={repo.is_active ? faFolderOpen : faFolder}
title={repo.name}
subtitle={repo.owner ? repo.owner.username : null}
onClick={onRepoClick}
upperLabel={strings.created}
upperValue={repo.start ? new Date(repo.start).toLocaleString() : null}
lowerLabel={strings.archived}
lowerValue={repo.end ? new Date(repo.end).toLocaleString() : null}
buttons={buttons}
{...props}
/>
<SummaryRight/>
</SummaryBase>
)
}

View file

@ -1,18 +1,28 @@
import React, { useContext } from "react"
import Summary from "../base/Summary"
import { faStar, faTrash, faUser } from "@fortawesome/free-solid-svg-icons"
import Button from "../base/Button"
import ContextUser from "../../contexts/ContextUser"
import ContextLanguage from "../../contexts/ContextLanguage"
import SummaryBase from "../base/summary/SummaryBase"
import SummaryLeft from "../base/summary/SummaryLeft"
import SummaryLabels from "../base/summary/SummaryLabels"
import SummaryButton from "../base/summary/SummaryButton"
import SummaryRight from "../base/summary/SummaryRight"
export default function SummaryUser({ user, destroyUser, running, ...props }) {
const { user: loggedUser } = useContext(ContextUser)
const { strings } = useContext(ContextLanguage)
const buttons = <>
{loggedUser.email !== user.email ?
<Button
return (
<SummaryBase>
<SummaryLeft
icon={user.isAdmin ? faStar : faUser}
title={user.username}
subtitle={user.email}
/>
<SummaryLabels
upperLabel={strings.type}
upperValue={user.isAdmin ? strings.admin : strings.user}
/>
<SummaryButton
color={"Red"}
icon={faTrash}
onClick={async event => {
@ -23,19 +33,8 @@ export default function SummaryUser({ user, destroyUser, running, ...props }) {
disabled={running}
>
{strings.delete}
</Button>
: null}
</>
return (
<Summary
icon={user.isAdmin ? faStar : faUser}
title={user.username}
subtitle={user.email}
upperLabel={strings.type}
upperValue={user.isAdmin ? strings.admin : strings.user}
buttons={buttons}
{...props}
/>
</SummaryButton>
<SummaryRight/>
</SummaryBase>
)
}

View file

@ -83,3 +83,15 @@ h1, h2, h3, h4, h5, h6 {
--outline: black;
}
.Clickable {
cursor: pointer;
}
.Clickable:hover {
filter: brightness(110%);
}
.Clickable:active {
filter: brightness(125%);
}