1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-10-16 20:17:25 +00:00

📔 Document _everything_ and refactor some things

This commit is contained in:
Stefano Pigozzi 2021-04-23 02:18:06 +02:00
parent 449611f805
commit 2147e6c374
Signed by untrusted user who does not match committer: steffo
GPG key ID: 6965406171929D01
18 changed files with 192 additions and 66 deletions

View file

@ -13,6 +13,12 @@ import PageSandbox from "./routes/PageSandbox"
import useSavedTheme from "./hooks/useSavedTheme"
/**
* The main component of the webapp, the root of the render tree, what is displayed when the web page is visited.
*
* @returns {JSX.Element}
* @constructor
*/
export default function App() {
const [theme, setAndSaveTheme] = useSavedTheme();

View file

@ -3,6 +3,15 @@ import Style from "./BoxHeaderOnly.module.css"
import classNames from "classnames"
/**
* A box with only the header part and with no body.
*
* @param children - What should be displayed inside the header.
* @param className - Additional class(es) that should be added to the box.
* @param props - Additional props to pass to the box.
* @returns {JSX.Element}
* @constructor
*/
export default function BoxHeaderOnly({ children, className, ...props }) {
return (
<div className={classNames(Style.BoxHeaderOnly, className)} {...props}>

View file

@ -1,47 +1,26 @@
import React, {isValidElement} from "react"
import React from "react"
import Style from "./BoxWithHeader.module.css"
import classNames from "classnames"
import isString from "is-string"
export default function BoxWithHeader({ header, body, children, className, ...props }) {
if(isValidElement(header) || isString(header)) {
header = {
children: header
}
}
if(isValidElement(body) || isString(body)) {
body = {
children: body
}
}
if(header === undefined) {
header = {}
}
if(body === undefined) {
body = {}
}
if(children) {
if(body.children) {
throw new Error("If directly passing `children` to BoxWithHeader, body.children should not be set.")
}
body["children"] = children
}
/**
* A box with both a header and a body.
*
* @param header - The contents of the box header.
* @param children - The contents of the box body.
* @param className - Additional class(es) that should be added to the outer `<div>` of the box.
* @param props - Additional props to pass to the box.
* @returns {JSX.Element}
* @constructor
*/
export default function BoxWithHeader({ header, children, className, ...props }) {
return (
<div className={classNames(Style.BoxWithHeader, className)} {...props}>
<div className={classNames(Style.BoxHeader, header.className)}>
{header.children}
<div className={Style.BoxHeader}>
{header}
</div>
<div className={classNames(Style.BoxBody, body.className)}>
{body.children}
<div className={Style.BoxBody}>
{children}
</div>
</div>
)

View file

@ -4,8 +4,18 @@ import classNames from "classnames"
import make_icon from "../utils/make_icon"
/**
* A clickable button.
*
* @param children - The contents of the button.
* @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.
* @param props - Additional props to pass to the button.
* @returns {JSX.Element}
* @constructor
*/
export default function Button({ children, className, color, icon, ...props }) {
return (
<button type={"button"} className={classNames(Style.Button, Style[`Button${color}`], className)} {...props}>
{children} {make_icon(icon, Style.Icon)}

View file

@ -6,6 +6,17 @@ import { Link } from "react-router-dom"
import { useRouteMatch } from "react-router"
/**
* A button residing in the sidebar, used to switch between pages.
*
* @param icon - The FontAwesome IconDefinition of the icon that should be rendered in the button.
* @param children - The contents of the button.
* @param to - The path of the page the user should be redirected to when clicking on the button.
* @param className - Additional class(es) to add to the button.
* @param props - Additional props to pass to the button.
* @returns {JSX.Element}
* @constructor
*/
export default function ButtonSidebar({ icon, children, to, className, ...props }) {
const match = useRouteMatch({
path: to,

View file

@ -3,10 +3,16 @@ import Style from "./Checkbox.module.css"
import classNames from "classnames"
export default function Checkbox({ children, className, ...props }) {
/**
* A checkbox.
*
* @param className - Additional class(es) to add to the checkbox.
* @param props - Additional props to pass to the checkbox.
* @returns {JSX.Element}
* @constructor
*/
export default function Checkbox({ className, ...props }) {
return (
<input type={"checkbox"} className={classNames(Style.Checkbox, className)} {...props}>
{children}
</input>
<input type={"checkbox"} className={classNames(Style.Checkbox, className)} {...props} />
)
}

View file

@ -3,10 +3,16 @@ import Style from "./Input.module.css"
import classNames from "classnames"
export default function Input({ children, className, ...props }) {
/**
* A single-line box where the user can enter text.
*
* @param className - Additional class(es) to add to the element.
* @param props - Additional props to pass to the element.
* @returns {JSX.Element}
* @constructor
*/
export default function Input({ className, ...props }) {
return (
<input className={classNames(Style.Input, className)} {...props}>
{children}
</input>
<input className={classNames(Style.Input, className)} {...props} />
)
}

View file

@ -4,29 +4,41 @@ import classNames from "classnames"
import make_icon from "../utils/make_icon"
export default function InputWithIcon({ icon, className, style, onFocus, onBlur, ...props }) {
/**
* Like an {@link Input}, but with an icon on the left side.
*
* Has a state which stores whether the inner input element is focused or not to change the style of the whole element
* on focus.
*
* @param icon - The FontAwesome IconDefinition of the icon that should be rendered in the input.
* @param className - Additional class(es) to be added to the outer container.
* @param props - Additional props to be passed to the outer container.
* @returns {JSX.Element}
* @constructor
*/
export default function InputWithIcon({ icon, className, ...props }) {
const [isFocused, setFocused] = useState(false);
return (
<div className={classNames(Style.InputWithIcon, isFocused ? Style.Focused : null, className)} style={style}>
<div className={classNames(Style.IconPart)}>
<div className={classNames(Style.InputWithIcon, isFocused ? Style.Focused : null, className)}>
<div className={Style.IconPart}>
{make_icon(icon)}
</div>
<input
className={classNames(Style.InputPart)}
className={Style.InputPart}
onFocus={
event => {
setFocused(true)
if(onFocus) {
onFocus(event)
if(props.onFocus) {
props.onFocus(event)
}
}
}
onBlur={
event => {
setFocused(false)
if(onBlur) {
onBlur(event)
if(props.onBlur) {
props.onBlur(event)
}
}
}

View file

@ -1,8 +1,17 @@
import React, { Fragment } from "react"
import Style from "./Label.module.css"
import classNames from "classnames"
/**
* A row of a {@link LabelledForm}.
* It displays a label on the first column and a container for the labelled element on the second column.
*
* @param children - The labelled element.
* @param text - Text to be displayed in the label.
* @param for_ - The `[id]` of the labelled element.
* @returns {JSX.Element}
* @constructor
*/
export default function Label({ children, text, for_ }) {
return (
<Fragment>

View file

@ -3,6 +3,17 @@ import Style from "./LabelledForm.module.css"
import classNames from "classnames"
/**
* A form with two columns: the leftmost one contains labels, while the rightmost one contains input elements.
*
* The {@link Label} element can be used to quickly generate labelled input elements.
*
* @param children - The contents of the form.
* @param className - Additional class(es) to be added to the form.
* @param props - Additional props to be passed to the form.
* @returns {JSX.Element}
* @constructor
*/
export default function LabelledForm({ children, className, ...props }) {
return (
<form className={classNames(Style.LabelledForm, className)} {...props}>

View file

@ -4,6 +4,15 @@ import classNames from "classnames"
import Sidebar from "./Sidebar"
/**
* The base page layout, consisting of a {@link Sidebar} on the left and the page contents on the remaining space.
*
* @param children - The page contents.
* @param className - Additional class(es) to be added to the grid container.
* @param props - Additional props to be passed to the grid container.
* @returns {JSX.Element}
* @constructor
*/
export default function Layout({ children, className, ...props }) {
return (
<div className={classNames(Style.Layout, className)} {...props}>

View file

@ -1,10 +1,24 @@
import React, {useContext} from "react"
import Style from "./Logo.module.css"
import LogoDark from "../media/LogoDark.png"
import LogoLight from "../media/LogoLight.png"
import ContextTheme from "../contexts/ContextTheme"
import classNames from "classnames"
export default function Logo({ children, className, ...props }) {
/**
* The N.E.S.T. logo.
*
* It loads a different image based on the currently selected theme.
*
* @param className - Additional class(es) to add to the image.
* @param props - Additional props to pass to the image.
* @returns {JSX.Element}
* @constructor
*/
export default function Logo({ className, ...props }) {
// I have no idea why IntelliJ is complaining about this line
// It's perfectly fine!
const [theme, ] = useContext(ContextTheme)
let logo;
@ -19,6 +33,6 @@ export default function Logo({ children, className, ...props }) {
}
return (
<img src={logo} alt={"N.E.S.T."} {...props}/>
<img src={logo} className={classNames(Style.Logo, className)} alt={"N.E.S.T."} {...props}/>
)
}

View file

@ -2,11 +2,16 @@ import React from "react"
import Style from "./Radio.module.css"
import classNames from "classnames"
export default function Radio({ children, className, ...props }) {
/**
* A radio button.
*
* @param className - Additional class(es) to add to the checkbox.
* @param props - Additional props to pass to the checkbox.
* @returns {JSX.Element}
* @constructor
*/
export default function Radio({ className, ...props }) {
return (
<input type={"radio"} className={classNames(Style.Radio, className)} {...props}>
{children}
</input>
<input type={"radio"} className={classNames(Style.Radio, className)} {...props} />
)
}

View file

@ -3,6 +3,15 @@ import Style from "./Select.module.css"
import classNames from "classnames"
/**
* A dropdown list.
*
* @param children - The contents of the dropdown list, which usually should be `<option>` or `<optgroup>` elements.
* @param className - Additional class(es) to add to the element.
* @param props - Additional props to pass to the element.
* @returns {JSX.Element}
* @constructor
*/
export default function Select({ children, className, ...props }) {
return (
<select className={classNames(Style.Select, className)} {...props}>

View file

@ -3,7 +3,15 @@ import Select from "./Select"
import ContextTheme from "../contexts/ContextTheme"
export default function SelectTheme({ children, ...props }) {
/**
* A {@link Select} which allows the user to choose between the various available themes, switching between them as soon
* as the user selects a different one.
*
* @param props - Additional props to pass to the {@link Select}.
* @returns {JSX.Element}
* @constructor
*/
export default function SelectTheme({ ...props }) {
const [theme, setTheme] = useContext(ContextTheme);
return (

View file

@ -6,10 +6,18 @@ import ButtonSidebar from "./ButtonSidebar"
import { faCog, faExclamationTriangle, faFolder, faHome, faWrench } from "@fortawesome/free-solid-svg-icons"
export default function Sidebar({ children, className, ...props }) {
/**
* The sidebar of the website, with the logo, buttons to switch between the various pages and a notification counter.
*
* @todo The notification counter is still missing.
* @param className - Additional class(es) to be added to the outer container.
* @param props - Additional props to be passed to the outer container.
* @returns {JSX.Element}
* @constructor
*/
export default function Sidebar({ className, ...props }) {
return (
<div className={classNames(Style.Sidebar, className)} {...props}>
{/* TODO: Aggiungere il logo qui! */}
<Logo/>
<ButtonSidebar to={"/"} icon={faHome}>Dashboard</ButtonSidebar>
<ButtonSidebar to={"/repositories"} icon={faFolder}>Repositories</ButtonSidebar>
@ -20,7 +28,6 @@ export default function Sidebar({ children, className, ...props }) {
<ButtonSidebar to={"/sandbox"} icon={faWrench}>Sandbox</ButtonSidebar>
: null
}
{children}
</div>
)
}

View file

@ -1,4 +1,12 @@
import {createContext} from "react";
/**
* A context containing a list with the currently selected theme and the function to set a different one.
*
* The function can be `undefined` if run outside a provider.
*
* @type {React.Context}
*/
const ContextTheme = createContext(["ThemeDark", undefined])
export default ContextTheme

View file

@ -1,6 +1,13 @@
import { useState } from "react"
/**
* Hook with the same API as {@link React.useState} which stores the user's current theme setting, and syncs it to the
* browser's {@link localStorage}.
*
* @todo Perhaps this could be refactored into a general "useLocalStorageState" component?
* @returns {[string, function]}
*/
export default function useSavedTheme() {
const loadTheme = () => {
if(localStorage) {