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