Components: refactored UI components to use new state. Minor changes to exports and props

This commit is contained in:
Paweł Malak 2021-11-09 13:46:07 +01:00
parent adc017c48d
commit 89d935e27f
16 changed files with 118 additions and 146 deletions

View File

@ -2,55 +2,45 @@ import { Fragment } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import classes from './ActionButton.module.css'; import classes from './ActionButton.module.css';
import Icon from '../../Icons/Icon/Icon'; import { Icon } from '../..';
interface ComponentProps { interface Props {
name: string; name: string;
icon: string; icon: string;
link?: string; link?: string;
handler?: () => void; handler?: () => void;
} }
const ActionButton = (props: ComponentProps): JSX.Element => { export const ActionButton = (props: Props): JSX.Element => {
const body = ( const body = (
<Fragment> <Fragment>
<div className={classes.ActionButtonIcon}> <div className={classes.ActionButtonIcon}>
<Icon icon={props.icon} /> <Icon icon={props.icon} />
</div> </div>
<div className={classes.ActionButtonName}> <div className={classes.ActionButtonName}>{props.name}</div>
{props.name}
</div>
</Fragment> </Fragment>
); );
if (props.link) { if (props.link) {
return ( return (
<Link <Link to={props.link} tabIndex={0}>
to={props.link}
tabIndex={0}>
{body} {body}
</Link> </Link>
) );
} else if (props.handler) { } else if (props.handler) {
return ( return (
<div <div
className={classes.ActionButton} className={classes.ActionButton}
onClick={props.handler} onClick={props.handler}
onKeyPress={(e) => { onKeyPress={(e) => {
if (e.key === 'Enter' && props.handler) props.handler() if (e.key === 'Enter' && props.handler) props.handler();
}} }}
tabIndex={0} tabIndex={0}
>{body} >
</div>
)
} else {
return (
<div
className={classes.ActionButton}>
{body} {body}
</div> </div>
) );
} else {
return <div className={classes.ActionButton}>{body}</div>;
} }
} };
export default ActionButton;

View File

@ -1,21 +1,16 @@
import classes from './Button.module.css'; import classes from './Button.module.css';
interface ComponentProps { interface Props {
children: string; children: string;
click?: any; click?: any;
} }
const Button = (props: ComponentProps): JSX.Element => { export const Button = (props: Props): JSX.Element => {
const { const { children, click } = props;
children,
click
} = props;
return ( return (
<button className={classes.Button} onClick={click ? click : () => {}} > <button className={classes.Button} onClick={click ? click : () => {}}>
{children} {children}
</button> </button>
) );
} };
export default Button;

View File

@ -1,15 +1,10 @@
import { ReactNode } from 'react';
import classes from './InputGroup.module.css'; import classes from './InputGroup.module.css';
interface ComponentProps { interface Props {
children: JSX.Element | JSX.Element[]; children: ReactNode;
} }
const InputGroup = (props: ComponentProps): JSX.Element => { export const InputGroup = (props: Props): JSX.Element => {
return ( return <div className={classes.InputGroup}>{props.children}</div>;
<div className={classes.InputGroup}> };
{props.children}
</div>
)
}
export default InputGroup;

View File

@ -1,31 +1,27 @@
import { SyntheticEvent } from 'react'; import { ReactNode, SyntheticEvent } from 'react';
import classes from './ModalForm.module.css'; import classes from './ModalForm.module.css';
import Icon from '../../Icons/Icon/Icon'; import { Icon } from '../..';
interface ComponentProps { interface ComponentProps {
children: JSX.Element | JSX.Element[]; children: ReactNode;
modalHandler?: () => void; modalHandler?: () => void;
formHandler: (e: SyntheticEvent<HTMLFormElement>) => void; formHandler: (e: SyntheticEvent<HTMLFormElement>) => void;
} }
const ModalForm = (props: ComponentProps): JSX.Element => { export const ModalForm = (props: ComponentProps): JSX.Element => {
const _modalHandler = (): void => { const _modalHandler = (): void => {
if (props.modalHandler) { if (props.modalHandler) {
props.modalHandler(); props.modalHandler();
} }
} };
return ( return (
<div className={classes.ModalForm}> <div className={classes.ModalForm}>
<div className={classes.ModalFormIcon} onClick={_modalHandler}> <div className={classes.ModalFormIcon} onClick={_modalHandler}>
<Icon icon='mdiClose' /> <Icon icon="mdiClose" />
</div> </div>
<form onSubmit={(e) => props.formHandler(e)}> <form onSubmit={(e) => props.formHandler(e)}>{props.children}</form>
{props.children}
</form>
</div> </div>
) );
} };
export default ModalForm;

View File

@ -1,18 +1,18 @@
import { Fragment } from 'react'; import { Fragment, ReactNode } from 'react';
import classes from './Headline.module.css'; import classes from './Headline.module.css';
interface ComponentProps { interface Props {
title: string; title: string;
subtitle?: string | JSX.Element; subtitle?: ReactNode;
} }
const Headline = (props: ComponentProps): JSX.Element => { export const Headline = (props: Props): JSX.Element => {
return ( return (
<Fragment> <Fragment>
<h1 className={classes.HeadlineTitle}>{props.title}</h1> <h1 className={classes.HeadlineTitle}>{props.title}</h1>
{props.subtitle && <p className={classes.HeadlineSubtitle}>{props.subtitle}</p>} {props.subtitle && (
<p className={classes.HeadlineSubtitle}>{props.subtitle}</p>
)}
</Fragment> </Fragment>
) );
} };
export default Headline;

View File

@ -2,17 +2,15 @@ import { Link } from 'react-router-dom';
import classes from './SectionHeadline.module.css'; import classes from './SectionHeadline.module.css';
interface ComponentProps { interface Props {
title: string; title: string;
link: string link: string;
} }
const SectionHeadline = (props: ComponentProps): JSX.Element => { export const SectionHeadline = (props: Props): JSX.Element => {
return ( return (
<Link to={props.link}> <Link to={props.link}>
<h2 className={classes.SectionHeadline}>{props.title}</h2> <h2 className={classes.SectionHeadline}>{props.title}</h2>
</Link> </Link>
) );
} };
export default SectionHeadline;

View File

@ -4,8 +4,6 @@ interface Props {
text: string; text: string;
} }
const SettingsHeadline = (props: Props): JSX.Element => { export const SettingsHeadline = (props: Props): JSX.Element => {
return <h2 className={classes.SettingsHeadline}>{props.text}</h2>; return <h2 className={classes.SettingsHeadline}>{props.text}</h2>;
}; };
export default SettingsHeadline;

View File

@ -1,6 +1,4 @@
.Icon { .Icon {
color: var(--color-primary); color: var(--color-primary);
/* for settings */
/* color: var(--color-background); */
width: 90%; width: 90%;
} }

View File

@ -2,12 +2,12 @@ import classes from './Icon.module.css';
import { Icon as MDIcon } from '@mdi/react'; import { Icon as MDIcon } from '@mdi/react';
interface ComponentProps { interface Props {
icon: string; icon: string;
color?: string; color?: string;
} }
const Icon = (props: ComponentProps): JSX.Element => { export const Icon = (props: Props): JSX.Element => {
const MDIcons = require('@mdi/js'); const MDIcons = require('@mdi/js');
let iconPath = MDIcons[props.icon]; let iconPath = MDIcons[props.icon];
@ -22,7 +22,5 @@ const Icon = (props: ComponentProps): JSX.Element => {
path={iconPath} path={iconPath}
color={props.color ? props.color : 'var(--color-primary)'} color={props.color ? props.color : 'var(--color-primary)'}
/> />
) );
} };
export default Icon;

View File

@ -1,39 +1,32 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { connect } from 'react-redux'; import { useSelector } from 'react-redux';
import { Skycons } from 'skycons-ts'; import { Skycons } from 'skycons-ts';
import { GlobalState, Theme } from '../../../../interfaces'; import { State } from '../../../../store/reducers';
import { IconMapping, TimeOfDay } from './IconMapping'; import { IconMapping, TimeOfDay } from './IconMapping';
interface ComponentProps { interface Props {
theme: Theme;
weatherStatusCode: number; weatherStatusCode: number;
isDay: number; isDay: number;
} }
const WeatherIcon = (props: ComponentProps): JSX.Element => { export const WeatherIcon = (props: Props): JSX.Element => {
const { theme } = useSelector((state: State) => state.theme);
const icon = props.isDay const icon = props.isDay
? new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.day) ? new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.day)
: new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.night); : new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.night);
useEffect(() => { useEffect(() => {
const delay = setTimeout(() => { const delay = setTimeout(() => {
const skycons = new Skycons({'color': props.theme.colors.accent}); const skycons = new Skycons({ color: theme.colors.accent });
skycons.add(`weather-icon`, icon); skycons.add(`weather-icon`, icon);
skycons.play(); skycons.play();
}, 1); }, 1);
return () => { return () => {
clearTimeout(delay); clearTimeout(delay);
} };
}, [props.weatherStatusCode, icon, props.theme.colors.accent]); }, [props.weatherStatusCode, icon, theme.colors.accent]);
return <canvas id={`weather-icon`} width='50' height='50'></canvas> return <canvas id={`weather-icon`} width="50" height="50"></canvas>;
} };
const mapStateToProps = (state: GlobalState) => {
return {
theme: state.theme.theme
}
}
export default connect(mapStateToProps)(WeatherIcon);

View File

@ -1,13 +1,10 @@
import { ReactNode } from 'react';
import classes from './Layout.module.css'; import classes from './Layout.module.css';
interface ComponentProps { interface ComponentProps {
children: JSX.Element | JSX.Element[]; children: ReactNode;
} }
export const Container = (props: ComponentProps): JSX.Element => { export const Container = (props: ComponentProps): JSX.Element => {
return ( return <div className={classes.Container}>{props.children}</div>;
<div className={classes.Container}> };
{props.children}
</div>
)
}

View File

@ -1,28 +1,29 @@
import { MouseEvent, useRef } from 'react'; import { MouseEvent, ReactNode, useRef } from 'react';
import classes from './Modal.module.css'; import classes from './Modal.module.css';
interface ComponentProps { interface Props {
isOpen: boolean; isOpen: boolean;
setIsOpen: Function; setIsOpen: Function;
children: JSX.Element; children: ReactNode;
} }
const Modal = (props: ComponentProps): JSX.Element => { export const Modal = (props: Props): JSX.Element => {
const modalRef = useRef(null); const modalRef = useRef(null);
const modalClasses = [classes.Modal, props.isOpen ? classes.ModalOpen : classes.ModalClose].join(' '); const modalClasses = [
classes.Modal,
props.isOpen ? classes.ModalOpen : classes.ModalClose,
].join(' ');
const clickHandler = (e: MouseEvent) => { const clickHandler = (e: MouseEvent) => {
if (e.target === modalRef.current) { if (e.target === modalRef.current) {
props.setIsOpen(false); props.setIsOpen(false);
} }
} };
return ( return (
<div className={modalClasses} onClick={clickHandler} ref={modalRef}> <div className={modalClasses} onClick={clickHandler} ref={modalRef}>
{props.children} {props.children}
</div> </div>
) );
} };
export default Modal;

View File

@ -1,18 +1,21 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { connect } from 'react-redux'; import { useDispatch } from 'react-redux';
import { clearNotification } from '../../../store/actions'; import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
import classes from './Notification.module.css'; import classes from './Notification.module.css';
interface ComponentProps { interface Props {
title: string; title: string;
message: string; message: string;
id: number; id: number;
url: string | null; url: string | null;
clearNotification: (id: number) => void;
} }
const Notification = (props: ComponentProps): JSX.Element => { export const Notification = (props: Props): JSX.Element => {
const dispatch = useDispatch();
const { clearNotification } = bindActionCreators(actionCreators, dispatch);
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const elementClasses = [ const elementClasses = [
classes.Notification, classes.Notification,
@ -24,13 +27,13 @@ const Notification = (props: ComponentProps): JSX.Element => {
setIsOpen(false); setIsOpen(false);
}, 3500); }, 3500);
const clearNotification = setTimeout(() => { const clearNotificationTimeout = setTimeout(() => {
props.clearNotification(props.id); clearNotification(props.id);
}, 3600); }, 3600);
return () => { return () => {
window.clearTimeout(closeNotification); window.clearTimeout(closeNotification);
window.clearTimeout(clearNotification); window.clearTimeout(clearNotificationTimeout);
}; };
}, []); }, []);
@ -48,5 +51,3 @@ const Notification = (props: ComponentProps): JSX.Element => {
</div> </div>
); );
}; };
export default connect(null, { clearNotification })(Notification);

View File

@ -1,11 +1,9 @@
import classes from './Spinner.module.css'; import classes from './Spinner.module.css';
const Spinner = (): JSX.Element => { export const Spinner = (): JSX.Element => {
return ( return (
<div className={classes.SpinnerWrapper}> <div className={classes.SpinnerWrapper}>
<div className={classes.Spinner}>Loading...</div> <div className={classes.Spinner}>Loading...</div>
</div> </div>
) );
} };
export default Spinner;

View File

@ -1,26 +1,26 @@
import classes from './Table.module.css'; import classes from './Table.module.css';
interface ComponentProps { interface Props {
children: JSX.Element | JSX.Element[]; children: React.ReactNode;
headers: string[]; headers: string[];
innerRef?: any; innerRef?: any;
} }
const Table = (props: ComponentProps): JSX.Element => { export const Table = (props: Props): JSX.Element => {
return ( return (
<div className={classes.TableContainer} ref={props.innerRef}> <div className={classes.TableContainer} ref={props.innerRef}>
<table className={classes.Table}> <table className={classes.Table}>
<thead className={classes.TableHead}> <thead className={classes.TableHead}>
<tr> <tr>
{props.headers.map((header: string, index: number): JSX.Element => (<th key={index}>{header}</th>))} {props.headers.map(
(header: string, index: number): JSX.Element => (
<th key={index}>{header}</th>
)
)}
</tr> </tr>
</thead> </thead>
<tbody className={classes.TableBody}> <tbody className={classes.TableBody}>{props.children}</tbody>
{props.children}
</tbody>
</table> </table>
</div> </div>
) );
} };
export default Table;

View File

@ -0,0 +1,14 @@
export * from './Table/Table';
export * from './Spinner/Spinner';
export * from './Notification/Notification';
export * from './Modal/Modal';
export * from './Layout/Layout';
export * from './Icons/Icon/Icon';
export * from './Icons/WeatherIcon/WeatherIcon';
export * from './Headlines/Headline/Headline';
export * from './Headlines/SectionHeadline/SectionHeadline';
export * from './Headlines/SettingsHeadline/SettingsHeadline';
export * from './Forms/InputGroup/InputGroup';
export * from './Forms/ModalForm/ModalForm';
export * from './Buttons/ActionButton/ActionButton';
export * from './Buttons/Button/Button';