Random quote generator for inspirational quotes.
Covers the following concepts:
React + React Hooks CSS Grid + Flexbox Asynchronous API calls in Javascript
Limited usage of:
SASS/SCSS Bootstrap
<link | |
rel="stylesheet" | |
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" | |
/> | |
<link href="https://fonts.googleapis.com/css2?family=Bad+Script&display=swap" rel="stylesheet"> | |
<div id="root"></div> |
const { useState, useEffect, useCallback } = React; | |
//generic data fetcher | |
const fetchData = (url) => { | |
const [data, setData] = useState(null); | |
const [loading, setLoading] = useState(true); | |
useEffect(() => { | |
fetch(url) | |
.then(function(response) { | |
return response.json(); | |
}) | |
.then(function(data) { | |
// let randInt = Math.floor(Math.random() * data.length); | |
// setData(data[randInt]); | |
setData(data); | |
setLoading(false); | |
}); | |
}, [url]); | |
return {data, loading}; | |
} | |
//outputs text and author of randomly generated quote | |
function QuoteComponent() { | |
const {data, loading} = fetchData("https://type.fit/api/quotes"); | |
const [randInt, setRandInt] = useState(Math.floor(Math.random() * 1000)); | |
const fetchQuote = useCallback(() => { | |
setRandInt(Math.floor(Math.random() * data.length)); | |
}); | |
return ( | |
<> | |
<div className="format-text text-area"> | |
{loading ? (<div> is loading... </div>) : (<div> | |
<p id="text">{data[randInt].text}</p> | |
<p id="author">{data[randInt].author && "-"}{data[randInt].author}</p> | |
</div>) | |
} | |
</div> | |
<div class="button-area"> | |
<Socials quote={loading ? "" :data[randInt]}/> | |
<div class="submit"> | |
<button id="new-quote" type="submit" class="button button-hover" onClick={fetchQuote}>new quote</button> | |
</div> | |
</div> | |
</> | |
); | |
}; | |
ReactDOM.render( | |
<App />, | |
document.getElementById('root') | |
); | |
function Socials(props) { | |
return ( | |
<a id="tweet-quote" href="https://twitter.com/intent/tweet" title="Tweet this quote!" target="_blank" class="social button-hover" | |
href="https://twitter.com/intent/tweet" | |
data-text={props.quote.text + ". As originally said from " + props.quote.author} | |
data-url="https://codepen.io/klixfe-the-animator/pen/RwRWWmj?editors=1111" | |
data-hashtags="inspirational"> | |
<i class="fa fa-twitter"></i> | |
</a>) | |
}; | |
function App(){ | |
return ( | |
<div class="grid-container center"> | |
<div class="header"> | |
</div> | |
<div id="quote-box" class="content"> | |
<QuoteComponent /> | |
</div> | |
<div class="footer"> | |
</div> | |
</div> | |
) | |
}; |
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.1/react-redux.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.3.0/redux-thunk.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script> |
$primary-font: Bad Script; | |
$tb-color: #6f649b; | |
$primary-color: #8C83AF; | |
$font-color: white; | |
$border-radius: 10px; | |
.grid-container { | |
display: grid; | |
grid-template-rows: [header] 25px [body] auto [footer] 25px; | |
grid-template-columns: [left-gutter] 1fr [content] 4fr [right-gutter] 1fr; | |
row-gap: 2px; | |
} | |
.content { | |
grid-row: body; | |
grid-column: content; | |
background-color: $primary-color; | |
display: grid; | |
grid-template-columns: [left-gutter] 50px [main-body] 4fr [right-gutter] 50px; | |
grid-template-rows: [text-area] 3fr [button-area] 1fr; | |
} | |
.footer { | |
grid-row: footer; | |
grid-column: content / right-gutter; | |
background-color: $tb-color; | |
border-radius: 0px 0px $border-radius $border-radius; | |
} | |
.header { | |
grid-column: content / right-gutter; | |
grid-row: header; | |
background-color: $tb-color; | |
border-radius: $border-radius $border-radius 0px 0px; | |
} | |
.text-area { | |
grid-row: text-area; | |
grid-column: main-body / right-gutter; | |
padding: 0px 10px 0px 10px; | |
font-family: $primary-font; | |
color: $font-color; | |
font-size: 1.5em; | |
font-weight: 700; | |
} | |
.button-area { | |
grid-row: button-area / right-gutter; | |
grid-column: main-body; | |
display: flex; | |
justify-content: space-between; | |
align-content: center; | |
padding-bottom: 1em; | |
} | |
.social { | |
align-self: center; | |
color: $font-color; | |
font-size: 2em; | |
background-color: $tb-color; | |
border-radius: $border-radius; | |
padding: 0px 5px 0px 5px; | |
} | |
.submit { | |
align-self: center; | |
} | |
.format-text { | |
text-align: right; | |
} | |
.center { | |
text-align: center; | |
} | |
.button { | |
font-family: $primary-font; | |
font-size: 1.25em; | |
background-color: $tb-color; | |
color: $font-color; | |
border-radius: $border-radius; | |
border: none; | |
padding: 0px 10px 0px 10px; | |
} | |
.button-hover { | |
cursor: pointer; | |
&:hover { | |
filter: brightness(60%); | |
} | |
} |