11import React , { useEffect , useMemo , useState } from 'react' ;
22import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ;
33import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes' ;
4- import { CSSTransition } from 'react-transition-group' ;
54import Spinner from '@/components/elements/Spinner' ;
6- import classNames from 'classnames' ;
5+ import tw from 'twin.macro' ;
6+ import styled from 'styled-components/macro' ;
7+ import Fade from '@/components/elements/Fade' ;
78
89export interface RequiredModalProps {
910 visible : boolean ;
@@ -12,73 +13,86 @@ export interface RequiredModalProps {
1213 top ?: boolean ;
1314}
1415
15- type Props = RequiredModalProps & {
16+ interface Props extends RequiredModalProps {
1617 dismissable ?: boolean ;
1718 closeOnEscape ?: boolean ;
1819 closeOnBackground ?: boolean ;
1920 showSpinnerOverlay ?: boolean ;
20- children : React . ReactNode ;
2121}
2222
23- export default ( { visible, appear, dismissable, showSpinnerOverlay, top = true , closeOnBackground = true , closeOnEscape = true , onDismissed, children } : Props ) => {
24- const [ render , setRender ] = useState ( visible ) ;
23+ const ModalMask = styled . div `
24+ ${ tw `fixed z-50 overflow-auto flex w-full inset-0` } ;
25+ background: rgba(0, 0, 0, 0.70);
26+ ` ;
27+
28+ const ModalContainer = styled . div < { alignTop ?: boolean } > `
29+ ${ tw `relative flex flex-col w-full m-auto` } ;
30+ max-width: 50%;
31+ // @todo max-w-screen-lg perhaps?
32+ ${ props => props . alignTop && 'margin-top: 10%' } ;
33+
34+ & > .close-icon {
35+ ${ tw `absolute right-0 p-2 text-white cursor-pointer opacity-50 transition-all duration-150 ease-linear hover:opacity-100` } ;
36+ top: -2rem;
37+
38+ &:hover {${ tw `transform rotate-90` } }
39+ }
40+ ` ;
41+
42+ const Modal : React . FC < Props > = ( { visible, appear, dismissable, showSpinnerOverlay, top = true , closeOnBackground = true , closeOnEscape = true , onDismissed, children } ) => {
43+ const [ render , setRender ] = useState ( visible ) ;
2544
2645 const isDismissable = useMemo ( ( ) => {
2746 return ( dismissable || true ) && ! ( showSpinnerOverlay || false ) ;
28- } , [ dismissable , showSpinnerOverlay ] ) ;
47+ } , [ dismissable , showSpinnerOverlay ] ) ;
2948
3049 const handleEscapeEvent = ( e : KeyboardEvent ) => {
3150 if ( isDismissable && closeOnEscape && e . key === 'Escape' ) {
3251 setRender ( false ) ;
3352 }
3453 } ;
3554
36- useEffect ( ( ) => {
37- setRender ( visible ) ;
38- } , [ visible ] ) ;
55+ useEffect ( ( ) => setRender ( visible ) , [ visible ] ) ;
3956
4057 useEffect ( ( ) => {
4158 window . addEventListener ( 'keydown' , handleEscapeEvent ) ;
4259
4360 return ( ) => window . removeEventListener ( 'keydown' , handleEscapeEvent ) ;
44- } , [ render ] ) ;
61+ } , [ render ] ) ;
4562
4663 return (
47- < CSSTransition
48- timeout = { 250 }
49- classNames = { 'fade' }
50- appear = { appear }
51- in = { render }
52- unmountOnExit = { true }
53- onExited = { ( ) => onDismissed ( ) }
54- >
55- < div className = { 'modal-mask' } onClick = { e => {
56- if ( isDismissable && closeOnBackground ) {
57- e . stopPropagation ( ) ;
58- if ( e . target === e . currentTarget ) {
59- setRender ( false ) ;
64+ < Fade timeout = { 250 } appear = { appear } in = { render } unmountOnExit onExited = { onDismissed } >
65+ < ModalMask
66+ onClick = { e => {
67+ if ( isDismissable && closeOnBackground ) {
68+ e . stopPropagation ( ) ;
69+ if ( e . target === e . currentTarget ) {
70+ setRender ( false ) ;
71+ }
6072 }
61- }
62- } } >
63- < div className = { classNames ( 'modal-container' , { top } ) } >
73+ } }
74+ >
75+ < ModalContainer alignTop = { top } >
6476 { isDismissable &&
65- < div className = { 'modal- close-icon' } onClick = { ( ) => setRender ( false ) } >
77+ < div className = { 'close-icon' } onClick = { ( ) => setRender ( false ) } >
6678 < FontAwesomeIcon icon = { faTimes } />
6779 </ div >
6880 }
6981 { showSpinnerOverlay &&
7082 < div
71- className = { ' absolute w-full h-full rounded flex items-center justify-center' }
83+ css = { tw ` absolute w-full h-full rounded flex items-center justify-center` }
7284 style = { { background : 'hsla(211, 10%, 53%, 0.25)' } }
7385 >
7486 < Spinner />
7587 </ div >
7688 }
77- < div className = { 'modal-content p-6' } >
89+ < div css = { tw `bg-neutral-800 p-6 rounded shadow-md overflow-y-scroll transition-all duration-250` } >
7890 { children }
7991 </ div >
80- </ div >
81- </ div >
82- </ CSSTransition >
92+ </ ModalContainer >
93+ </ ModalMask >
94+ </ Fade >
8395 ) ;
8496} ;
97+
98+ export default Modal ;
0 commit comments