<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->
<div class="app">
<div class="form">
<input placeholder="Who are you looking for?" id="username">
<input type="button" value="Search" id="search">
</div>
<div class="profile"></div>
<div class="alert">Alert Message</div>
</div>
<script type="text/x-tmpl" id="template">
<div class="header">
<div class="avatar"></div>
<h2 class="name">{%= o.name %}</h2>
<h3 class="location">{%= o.location %}</h3>
</div>
<div class="footer">
<ul class="details">
<li>Repositories<span>{%= o.public_repos %}</span></li>
<li>Followers<br><span>{%= o.followers %}</span></li>
<li>Following<br><span>{%= o.following %}</span></li>
</ul>
<a href="{%= o.html_url %}" class="to-github">go to github</a>
</div>
</script>
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #383838;
}
.app {
width: 320px;
height: 630px;
font-family: sans-serif;
position: relative;
overflow: hidden;
}
/* form */
.form {
height: 50px;
background-color: rgba(255, 255, 255, 0.2);
border-radius: 2px;
box-sizing: border-box;
padding: 8px;
display: flex;
}
input {
border: none;
font-size: 14px;
outline: none;
border-radius: inherit;
padding: 0 8px;
}
#username {
flex-grow: 1;
background-color: rgba(255, 255, 255, 0.9);
color: #42454e;
}
#search {
margin-left: 8px;
background-color: rgba(0, 97, 145, 0.75);
color: rgba(255, 255, 255, 0.8);
font-weight: bold;
cursor: pointer;
}
#search:hover {
background-color: rgba(0, 97, 145, 0.45);
}
#search:active {
transform: scale(0.98);
background-color: rgba(0, 97, 145, 0.75);
}
/* profile card */
.profile {
width: 320px;
margin: 20px 0 0 0;
display: flex;
flex-direction: column;
--avatar: url(https://avatars3.githubusercontent.com/u/583231?v=4);
border-radius: 5px;
position: absolute;
overflow: hidden;
}
.profile::before {
content: '';
position: absolute;
width: calc(100% + 20px * 2);
height: calc(100% + 20px * 2);
background-image: var(--avatar);
background-size: cover;
z-index: -1;
margin: -20px;
filter: blur(10px);
}
.header {
height: 380px;
background-color: rgba(0, 97, 145, 0.45);
display: flex;
flex-direction: column;
align-items: center;
}
.avatar {
width: 140px;
height: 140px;
background-image: var(--avatar);
margin: 70px 0 0 0;
background-position: center;
background-size: cover;
border-radius: 50%;
box-shadow:
0 0 0 0.8em rgba(0, 0, 0, 0.2),
0 0 0 1em rgba(161, 220, 255, 0.35);
}
.name {
margin: 50px 0 0 0;
color: white;
font-size: 28px;
font-weight: normal;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.location {
margin: 5px 0 0 0;
color: rgba(255, 255, 255, 0.75);
font-weight: normal;
}
.footer {
height: 180px;
background-color: rgba(0, 97, 145, 0.75);
display: flex;
flex-direction: column;
align-items: center;
}
.details {
list-style-type: none;
padding: 0;
display: flex;
margin: 40px 0 0 0;
}
.details li {
color: rgba(255, 255, 255, 0.6);
text-align: center;
padding: 0 6px;
}
.details li span {
display: block;
color: rgba(255, 255, 255, 0.8);
}
.details li:not(:first-child) {
border-left: 2px solid rgba(255, 255, 255, 0.15);
}
.to-github {
margin: 30px 0 0 0;
display: block;
width: 200px;
height: 40px;
background-color: rgba(255, 255, 255, 0.5);
text-align: center;
line-height: 40px;
color: rgba(0, 0, 0, 0.75);
text-decoration: none;
text-transform: uppercase;
border-radius: 20px;
transition: 0.3s;
}
.to-github:hover {
background-color: rgba(255, 255, 255, 0.8);
}
/* alert box */
.alert {
position: absolute;
top: 70px;
width: 50%;
height: 50px;
background-color: rgba(0, 0, 0, 0.5);
text-align: center;
line-height: 50px;
color: rgba(255, 255, 255, 0.8);
border-radius: 0 5px;
left: 100%;
}
let mockData = {
"avatar_url": "https://avatars2.githubusercontent.com/u/4306341?s=460&v=4",
"name": "HARUN PEHLİVAN",
"location": "SULUOVA ANASYA TR",
"public_repos": 454,
"followers": 10,
"following": 151,
"html_url": "https://github.com/harunpehlivan",
}
window.onload = update(mockData)
function render(container, data) {
container.innerHTML = tmpl('template', data)
container.style.setProperty('--avatar', `url(${data.avatar_url})`)
}
function getData(username) {
username = username.replace(/[ ]/g, '')
if (username == '') return;
let apiUrl = `https://api.github.com/users/${username}`
fetch(apiUrl)
.then(function(response) {
if(response.status != 200) {
throw new Error(response.statusText)
}
return response.json()
})
.then(update)
.catch(alert)
}
document.getElementById('search').addEventListener('click', () => {
getData(document.getElementById('username').value)
})
document.getElementById('username').addEventListener('keypress', (e) => {
if (e.charCode != 13) return;
getData(document.getElementById('username').value)
})
function update(data) {
let current = document.getElementsByClassName('profile')[0]
let isInitial = (current.innerHTML == '')
if (isInitial) {
render(current, data)
return
}
// create a new card
let next = document.createElement('div')
next.className = 'profile'
next.style.left = '100%'
render(next, data)
current.after(next)
// define animation properties
let animationOut = {
keyframes: [
{left: '0', opacity: 1, offset: 0},
{left: '-100%', opacity: 0, offset: 1}
],
options: {
duration: 500,
fill: 'forwards'
}
}
let animationIn = {
keyframes: [
{left: '100%', opacity: 0, offset: 0},
{left: '0', opacity: 1, offset: 1}
],
options: {
duration: 500,
fill: 'forwards',
delay: 500
}
}
// animate the out animation first,
// then animate the in animation
let anime = current.animate(animationOut.keyframes, animationOut.options)
anime.onfinish = () => {
current.remove()
next.animate(animationIn.keyframes, animationIn.options)
}
}
function alert(error) {
let keyframes = [
{left: '100%', offset: 0},
{left: '50%', offset: 0.2},
{left: '50%', offset: 0.8},
{left: '100%', offset: 1},
]
let options = {
duration: 2000,
fill: 'forwards',
}
let element = document.getElementsByClassName('alert')[0]
element.innerText = error.message
element.animate(keyframes, options)
}