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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,39 +1,32 @@
import { useEffect } from 'react';
import { connect } from 'react-redux';
import { useSelector } from 'react-redux';
import { Skycons } from 'skycons-ts';
import { GlobalState, Theme } from '../../../../interfaces';
import { State } from '../../../../store/reducers';
import { IconMapping, TimeOfDay } from './IconMapping';
interface ComponentProps {
theme: Theme;
interface Props {
weatherStatusCode: 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
? new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.day)
: new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.night);
useEffect(() => {
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.play();
}, 1);
return () => {
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>
}
const mapStateToProps = (state: GlobalState) => {
return {
theme: state.theme.theme
}
}
export default connect(mapStateToProps)(WeatherIcon);
return <canvas id={`weather-icon`} width="50" height="50"></canvas>;
};

View File

@ -1,13 +1,10 @@
import { ReactNode } from 'react';
import classes from './Layout.module.css';
interface ComponentProps {
children: JSX.Element | JSX.Element[];
children: ReactNode;
}
export const Container = (props: ComponentProps): JSX.Element => {
return (
<div className={classes.Container}>
{props.children}
</div>
)
}
return <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';
interface ComponentProps {
interface Props {
isOpen: boolean;
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 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) => {
if (e.target === modalRef.current) {
props.setIsOpen(false);
}
}
};
return (
<div className={modalClasses} onClick={clickHandler} ref={modalRef}>
{props.children}
</div>
)
}
export default Modal;
);
};

View File

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

View File

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

View File

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