Skip to content

Commit 0d09bf9

Browse files
committed
added modals too
1 parent 475087d commit 0d09bf9

File tree

3 files changed

+244
-68
lines changed

3 files changed

+244
-68
lines changed

src/App.js

Lines changed: 114 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,93 +8,139 @@ import {
88
} from 'react-router-dom';
99
import { connect } from 'react-redux';
1010
import { ThemeProvider } from 'styled-components';
11+
import { CSSTransition } from 'react-transition-group';
1112

1213
import whitetheme from '../config/theme/whitetheme';
1314
import darktheme from '../config/theme/darktheme';
1415
import GlobalStyles from './components/GlobalStyles';
16+
import Modal from './components/Modal';
1517

1618
import Home from './pages/Home';
1719
import About from './pages/About';
1820
import Topics from './pages/Topics';
1921
import Page404 from './pages/Page404';
2022

21-
const App = ({
22-
themes,
23-
changeTheme,
24-
}) => {
25-
let theme = {};
23+
class App extends React.Component {
24+
static loadTheme = (themes) => {
25+
let theme = {};
2626

27-
if (themes.selected.id === 'white_theme') {
28-
theme = whitetheme;
29-
} else if (themes.selected.id === 'dark_theme') {
30-
theme = darktheme;
27+
if (themes.selected.id === 'white_theme') {
28+
theme = whitetheme;
29+
} else if (themes.selected.id === 'dark_theme') {
30+
theme = darktheme;
31+
}
32+
33+
return theme;
34+
}
35+
36+
constructor(props) {
37+
super(props);
38+
39+
this.state = {
40+
showModal: false,
41+
};
3142
}
3243

33-
return (
34-
<ThemeProvider theme={theme}>
35-
<div>
36-
<GlobalStyles theme={theme} />
37-
<Router>
38-
{/* Please update the code below for your project requirements */}
39-
<div>
40-
<ul>
41-
<li>
42-
<Link to="/">Home</Link>
43-
</li>
44-
<li>
45-
<Link to="/about">About</Link>
46-
</li>
47-
<li>
48-
<Link to="/topics">Topics</Link>
49-
</li>
50-
<li>
51-
<Link to="/link-does-not-exist">404</Link>
52-
</li>
53-
</ul>
54-
55-
<hr />
56-
57-
<Switch>
58-
<Route exact path="/" component={Home} />
59-
<Route path="/about" component={About} />
60-
<Route path="/topics" component={Topics} />
61-
<Route component={Page404} />
62-
</Switch>
63-
64-
<hr />
65-
66-
<h3>Theme</h3>
67-
<ul>
68-
{
69-
themes.data.map(({ id, name }) => (
70-
<li key={id}>
71-
<button
72-
disabled={id === themes.selected.id}
73-
onClick={() => changeTheme(id)}
74-
>
75-
{name}
76-
</button>
77-
</li>
78-
))
79-
}
80-
</ul>
81-
82-
<hr />
83-
84-
<h3>Images from simply from: <code>~./assets/[image]</code></h3>
85-
<img src="/assets/social-sprite.png" alt="social icon sprites" />
86-
</div>
87-
</Router>
88-
</div>
89-
</ThemeProvider>
44+
onModalClose = () => this.setState({ showModal: false });
45+
46+
renderModal = () => (
47+
this.state.showModal ? (
48+
<Modal onClose={this.onModalClose}>
49+
<div>
50+
<header><h2>Modal header</h2></header>
51+
<hr />
52+
<article>
53+
<p>This is an example of a modal!</p>
54+
<p><code>Esc</code> works too, if onClose is passed ;)</p>
55+
</article>
56+
<hr />
57+
<footer><button onClick={this.onModalClose}>Close</button></footer>
58+
</div>
59+
</Modal>
60+
) : null
9061
);
91-
};
62+
63+
render() {
64+
const {
65+
themes,
66+
changeTheme,
67+
} = this.props;
68+
const theme = App.loadTheme(themes);
69+
70+
return (
71+
<ThemeProvider theme={theme}>
72+
<div>
73+
<GlobalStyles theme={theme} />
74+
<Router>
75+
{/* Please update the code below for your project requirements */}
76+
<div>
77+
<ul>
78+
<li>
79+
<Link to="/">Home</Link>
80+
</li>
81+
<li>
82+
<Link to="/about">About</Link>
83+
</li>
84+
<li>
85+
<Link to="/topics">Topics</Link>
86+
</li>
87+
<li>
88+
<Link to="/link-does-not-exist">404</Link>
89+
</li>
90+
</ul>
91+
92+
<hr />
93+
94+
<Switch>
95+
<Route exact path="/" component={Home} />
96+
<Route path="/about" component={About} />
97+
<Route path="/topics" component={Topics} />
98+
<Route component={Page404} />
99+
</Switch>
100+
101+
<hr />
102+
103+
<h3>Modal example (React Portal)</h3>
104+
{this.renderModal()}
105+
<button
106+
disabled={this.state.showModal}
107+
onClick={() => this.setState({ showModal: true })}
108+
>
109+
Open modal
110+
</button>
111+
112+
<h3>Theme</h3>
113+
<ul>
114+
{
115+
themes.data.map(({ id, name }) => (
116+
<li key={id}>
117+
<button
118+
disabled={id === themes.selected.id}
119+
onClick={() => changeTheme(id)}
120+
>
121+
{name}
122+
</button>
123+
</li>
124+
))
125+
}
126+
</ul>
127+
128+
<hr />
129+
130+
<h3>Images from simply from: <code>~./assets/[image]</code></h3>
131+
<img src="/assets/social-sprite.png" alt="social icon sprites" />
132+
</div>
133+
</Router>
134+
</div>
135+
</ThemeProvider>
136+
);
137+
}
138+
}
92139

93140
App.propTypes = {
94141
themes: PropTypes.object.isRequired,
95142
changeTheme: PropTypes.func.isRequired,
96143
};
97-
App.displayName = App;
98144

99145
/**
100146
* Wrap App with redux to access

src/components/Modal/index.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import PropTypes from 'prop-types';
4+
5+
import Styled from './styled';
6+
7+
export default class Modal extends React.Component {
8+
constructor(props) {
9+
super(props);
10+
11+
this.modalRoot = document.querySelector('#modal-root');
12+
this.el = document.createElement('div');
13+
14+
this.state = {
15+
showModal: false,
16+
};
17+
}
18+
19+
componentDidMount() {
20+
this.modalRoot.appendChild(this.el);
21+
22+
setTimeout(() => {
23+
this.setState({
24+
showModal: true,
25+
});
26+
}, 100);
27+
28+
if (this.props.onClose) {
29+
window.addEventListener('keyup', this.onKeyUp, false);
30+
}
31+
}
32+
33+
componentWillUnmount() {
34+
this.modalRoot.removeChild(this.el);
35+
if (this.props.onClose) {
36+
window.removeEventListener('keyup', this.onKeyUp);
37+
}
38+
}
39+
40+
onKeyUp = (e) => {
41+
if (e.keyCode === 27) {
42+
this.props.onClose();
43+
}
44+
}
45+
46+
wrapContent() {
47+
return (
48+
<Styled
49+
tabIndex="-1"
50+
role="dialog"
51+
showModal={this.state.showModal}
52+
>
53+
<Styled.Modal showModal={this.state.showModal}>
54+
{
55+
this.props.onClose &&
56+
<Styled.Close onClick={this.props.onClose}>(X)</Styled.Close>
57+
}
58+
<Styled.Content>
59+
{this.props.children}
60+
</Styled.Content>
61+
</Styled.Modal>
62+
</Styled>
63+
);
64+
}
65+
66+
render() {
67+
return ReactDOM.createPortal(
68+
this.wrapContent(),
69+
this.el,
70+
);
71+
}
72+
}
73+
74+
Modal.defaultProps = {
75+
onClose: false,
76+
};
77+
Modal.propTypes = {
78+
children: PropTypes.element.isRequired,
79+
onClose: PropTypes.oneOfType([
80+
PropTypes.func,
81+
PropTypes.bool,
82+
]),
83+
};

src/components/Modal/styled.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import styled from 'styled-components';
2+
3+
const Styled = styled.div`
4+
display: flex;
5+
justify-content: center;
6+
align-items: center;
7+
position: fixed;
8+
left: 0;
9+
right: 0;
10+
top: 0;
11+
height: 100%;
12+
height: 100vh;
13+
transition: background-color 0.3s ease;
14+
background-color: ${props => (props.showModal ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)')};
15+
`;
16+
17+
const Modal = styled.div`
18+
max-height: 80%;
19+
max-width: 80%;
20+
width: 600px;
21+
background: ${props => props.theme.colors.white};
22+
color: ${props => props.theme.colors.gray400};
23+
border-radius: 4px;
24+
padding: 3rem 1rem;
25+
position: relative;
26+
box-shadow: 0 40px 77px rgba(0, 0, 0, 0.22), 0 27px 24px rgba(0, 0, 0, 0.2);
27+
transition: all 0.3s ease;
28+
opacity: ${props => (props.showModal ? 1 : 0)};
29+
transform: ${props => (props.showModal ? 'scale(1) translateY(0)' : 'scale(0.1) translateY(250px)')};
30+
overflow-y: auto;
31+
`;
32+
33+
const Close = styled.span`
34+
cursor: pointer;
35+
position: absolute;
36+
right: 20px;
37+
top: 20px;
38+
`;
39+
const Content = styled.div`
40+
text-align: center;
41+
`;
42+
43+
Styled.Modal = Modal;
44+
Styled.Close = Close;
45+
Styled.Content = Content;
46+
47+
export { Styled as default };

0 commit comments

Comments
 (0)