Done
Seto Elkahfi committed
Jan 20, 2022 at 00:31 UTC
f8c974fa2ad3788eed46397802f850798248dccd
4 files changed
+100
-105
src/components/App.tsx
+4
-17
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
2
import Header from './Header';
3
import Main from './Main';
4
import FooterLinks from './FooterLinks';
5
-import { CSSProperties } from 'react';
5
import { IntlProvider, addLocaleData } from 'react-intl';
6
import { BrowserRouter } from 'react-router-dom';
7
import Firebase, { FirebaseContext } from '../components/Firebase';
@@ -20,20 +19,10 @@ let i18nConfig = {
19
messages: messages_en
20
};
21
23
-const mainStyle: CSSProperties = {
24
- position: 'absolute',
25
- top: 50,
26
- left: 50,
27
- background: 'rgba(0,0,0,0.65)',
28
- width: 300,
29
- textAlign: 'left',
30
- padding: 10
31
-};
32
-
22
type AppProps = {}
23
24
type AppState = {
36
- language: string
25
+ language: string
26
}
27
28
class App extends Component<AppProps, AppState> {
@@ -65,11 +54,9 @@ class App extends Component<AppProps, AppState> {
54
<IntlProvider locale={i18nConfig.language} messages={i18nConfig.messages}>
55
<BrowserRouter>
56
<FirebaseContext.Provider value={this.createFirebaseContextIfNeeded(i18nConfig.language)}>
68
- <div style={mainStyle}>
69
- <Header onChangeLanguage={this.onChangeLanguage.bind(this)}/>
70
- <Main />
71
- <FooterLinks />
72
- </div>
57
+ <Header onChangeLanguage={this.onChangeLanguage.bind(this)} />
58
+ <Main />
59
+ <FooterLinks />
60
</FirebaseContext.Provider>
61
</BrowserRouter>
62
</IntlProvider >
src/components/FooterLinks.tsx
+20
-13
@@ -1,12 +1,6 @@
1
import React from 'react';
2
-
3
-const footerStyle = {
4
- padding: '0 30 30 30',
5
- color: '#fff',
6
- font: '11pt "Helvetica Neue", "Helvetica", Arial, sans-serif',
7
- textShadow: '#000 0px 1px 0px',
8
- lineHeight: '200%'
9
-};
2
+import { Link } from 'react-router-dom';
3
+import { FormattedMessage } from 'react-intl';
4
5
const linkStyle = {
6
display: 'inline-block',
@@ -27,11 +21,24 @@ const linkStyle = {
21
};
22
23
const FooterLinks = () => (
30
- <footer style={footerStyle}>
31
- <a style={linkStyle} href="https://stackoverflow.com/users/1137814/seto" title="StackOverflow" target="_blank" rel="noopener noreferrer">stackoverflow</a>
32
- <a style={linkStyle} href="https://github.com/setoelkahfi" title="GitHub" target="_blank" rel="noopener noreferrer">github</a>
33
- <a style={linkStyle} href="https://medium.com/@setoelkahfi" title="Medium" target="_blank" rel="noopener noreferrer">medium</a>
34
- <a style={linkStyle} href="https://id.linkedin.com/in/setoelkahfi" title="LinkedIn" target="_blank" rel="noopener noreferrer">linkedin</a>
24
+ <footer className="text-muted py-5">
25
+ <div className="container">
26
+ <p className="float-end mb-1">
27
+ <a href="#" style={linkStyle}>Back to top</a>
28
+ </p>
29
+ <p className="mb-0">
30
+ <Link to='/about' style={linkStyle}>
31
+ <FormattedMessage id="header.about"
32
+ defaultMessage="About"
33
+ description="About link" />
34
+ </Link>
35
+ <Link to='/cv' style={linkStyle}>
36
+ <FormattedMessage id="header.cv"
37
+ defaultMessage="CV"
38
+ description="CV link" />
39
+ </Link>
40
+ </p>
41
+ </div>
42
</footer>
43
);
44
src/components/Header.tsx
+48
-53
@@ -1,66 +1,61 @@
1
-import React, { Component, CSSProperties } from 'react';
2
-import { Link } from 'react-router-dom';
1
+import React, { Component } from 'react';
2
import { FormattedMessage } from 'react-intl';
4
-import LanguageDropdown from './LanguageDropdown';
5
-
6
-const styleHeader = {
7
- height: 50
8
-}
9
-
10
-const linkStyle = {
11
- color: '#fff',
12
- font: '11pt "Helvetica Neue", "Helvetica", Arial, sans-serif',
13
-}
14
-
15
-const listStyle = {
16
- display: 'inline',
17
- margin: 4
18
-}
19
-
20
-const listStyleLanguage: CSSProperties = {
21
- display: 'inline',
22
- margin: 4,
23
- float: 'right'
24
-}
3
+import { Link } from 'react-router-dom';
4
5
type HeaderProps = {
6
onChangeLanguage: (language: string) => void
7
}
8
30
-type HeaderState = {}
9
+type HeaderState = {
10
+ expand: boolean
11
+}
12
13
class Header extends Component<HeaderProps, HeaderState> {
14
15
+ constructor(props: HeaderProps | Readonly<HeaderProps>) {
16
+ super(props);
17
+ this.state = { expand: false };
18
+ }
19
+
20
+ toggleExpand() {
21
+ this.setState({ expand: !this.state.expand })
22
+ }
23
+
24
render() {
35
- return(
36
- <header style={styleHeader}>
37
- <nav>
38
- <ul>
39
- <li style={listStyle}><Link to='/' style={linkStyle}>
40
- <FormattedMessage id="header.home"
41
- defaultMessage="Home"
42
- description="Home link"/>
43
- </Link></li>
44
- <li style={listStyle}><Link to='/about' style={linkStyle}>
45
- <FormattedMessage id="header.about"
46
- defaultMessage="About"
47
- description="About link"/>
48
- </Link></li>
49
- <li style={listStyle}><Link to='/cv' style={linkStyle}>
50
- <FormattedMessage id="header.cv"
51
- defaultMessage="CV"
52
- description="CV link"/>
53
- </Link></li>
54
- <li style={listStyle}><Link to='/contact' style={linkStyle}>
55
- <FormattedMessage id="header.contact"
56
- defaultMessage="Contact"
57
- description="Contact link"/>
58
- </Link></li>
59
- <li style={listStyleLanguage}>
60
- <LanguageDropdown onChangeLanguage={this.props.onChangeLanguage} />
61
- </li>
62
- </ul>
63
- </nav>
25
+ return (
26
+ <header>
27
+ <div className={`collapse bg-dark ${this.state.expand ? 'show' : ''}`} id="navbarHeader">
28
+ <div className="container">
29
+ <div className="row">
30
+ <div className="col-sm-8 col-md-7 py-4">
31
+ <h4 className="text-white">
32
+ <FormattedMessage id="about.title"
33
+ defaultMessage="About"
34
+ description="About page title" /></h4>
35
+ <p className="text-muted">We buy things we don't need, with money we don't have, to impress people we don't like.</p>
36
+ </div>
37
+ <div className="col-sm-4 offset-md-1 py-4">
38
+ <h4 className="text-white">Contact</h4>
39
+ <ul className="list-unstyled">
40
+ <li><a className="text-white" href="https://stackoverflow.com/users/1137814/seto" title="StackOverflow" target="_blank" rel="noopener noreferrer">StackOverflow</a></li>
41
+ <li><a className="text-white" href="https://github.com/setoelkahfi" title="GitHub" target="_blank" rel="noopener noreferrer">GitHub</a></li>
42
+ <li><a className="text-white" href="https://medium.com/@setoelkahfi" title="Medium" target="_blank" rel="noopener noreferrer">Medium</a></li>
43
+ <li><a className="text-white" href="https://id.linkedin.com/in/setoelkahfi" title="LinkedIn" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
44
+ </ul>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ <div className="navbar navbar-dark bg-dark shadow-sm">
50
+ <div className="container">
51
+ <Link to='/' className="navbar-brand d-flex align-items-center">
52
+ <strong>@seto</strong>
53
+ </Link>
54
+ <button className="navbar-toggler" onClick={this.toggleExpand.bind(this)} type="button" data-bs-toggle="collapse" data-bs-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded={this.state.expand} aria-label="Toggle navigation">
55
+ <span className="navbar-toggler-icon"></span>
56
+ </button>
57
+ </div>
58
+ </div>
59
</header>
60
)
61
}
src/components/Main.tsx
+28
-22
@@ -18,29 +18,35 @@ const mainStyle = {
18
19
const Main = () => (
20
<main style={mainStyle}>
21
- <Switch>
22
- <Route exact path='/'>
23
- <FirebaseContext.Consumer>
24
- {firebase => <Home firebase={firebase} />}
25
- </FirebaseContext.Consumer>
26
- </Route>
27
- <Route path='/about'>
28
- <FirebaseContext.Consumer>
29
- {firebase => <About firebase={firebase} />}
30
- </FirebaseContext.Consumer>
31
- </Route>
32
- <Route path='/cv'>
33
- <FirebaseContext.Consumer>
34
- {firebase => <Cv firebase={firebase} />}
35
- </FirebaseContext.Consumer>
36
- </Route>
21
+ <section className="py-5 text-center container">
22
+ <div className="row py-lg-5">
23
+ <div className="col-lg-6 col-md-8 mx-auto">
24
+ <Switch>
25
+ <Route exact path='/'>
26
+ <FirebaseContext.Consumer>
27
+ {firebase => <Home firebase={firebase} />}
28
+ </FirebaseContext.Consumer>
29
+ </Route>
30
+ <Route path='/about'>
31
+ <FirebaseContext.Consumer>
32
+ {firebase => <About firebase={firebase} />}
33
+ </FirebaseContext.Consumer>
34
+ </Route>
35
+ <Route path='/cv'>
36
+ <FirebaseContext.Consumer>
37
+ {firebase => <Cv firebase={firebase} />}
38
+ </FirebaseContext.Consumer>
39
+ </Route>
40
38
- <Route path='/contact'>
39
- <FirebaseContext.Consumer>
40
- {firebase => <Contact firebase={firebase} />}
41
- </FirebaseContext.Consumer>
42
- </Route>
43
- </Switch>
41
+ <Route path='/contact'>
42
+ <FirebaseContext.Consumer>
43
+ {firebase => <Contact firebase={firebase} />}
44
+ </FirebaseContext.Consumer>
45
+ </Route>
46
+ </Switch>
47
+ </div>
48
+ </div>
49
+ </section>
50
</main>
51
);
52