vuejs github timeline
by Victor Melnik
HTML
<div id="timeline">
<h1>GitHub Timeline</h1>
<div class="progress">
<div class="line" :style="{ width: (Math.floor(time * 100 / timeMax) + '%') }"></div>
<div class="background"></div>
</div>
<ul>
<li v-for="event in events">
<img class="avatar" :src="event.actor.avatar_url" />
<div class="headline">
<div class="time">{{ event.created_at|formatDate }}</div>
<a class="user" :href="'https://github.com/' + event.actor.login">{{ event.actor.login }}</a>
</div>
<div class="clearfix"></div>
<div v-if="event.type === 'CommitCommentEvent'">
commented commit in <repo :name="event.repo.name"></repo><br/>
<span class="small">{{ event.payload.comment.body }}</span>
</div>
<div v-else-if="event.type === 'CreateEvent'">
created {{ event.payload.ref_type }} <repo :name="event.repo.name"></repo>
</div>
<div v-else-if="event.type === 'DeleteEvent'">
deleted {{ event.payload.ref_type }} <repo :name="event.repo.name"></repo>
</div>
<div v-else-if="event.type === 'ForkEvent'">
forked repository <repo :name="event.repo.name"></repo>
</div>
<div v-else-if="event.type === 'IssueCommentEvent'">
commented issue {{ event.payload.issue.title }} on <repo :name="event.repo.name"></repo><br/>
<span class="small">{{ event.payload.comment.body }}</span>
</div>
<div v-else-if="event.type === 'IssuesEvent'">
{{ event.payload.action }} issue "{{ event.payload.issue.title }}" on <repo :name="event.repo.name"></repo>
</div>
<div v-else-if="event.type === 'PullRequestEvent'">
{{ event.payload.action }} pull request #{{ event.payload.number }} "{{ event.payload.pull_request.title }}" on <repo :name="event.repo.name"></repo>
</div>
<div v-else-if="event.type === 'PushEvent'">
pushed {{ event.payload.commits.length }} commits to <repo :name="event.repo.name"></repo>
...
CSS
body {
font-family: Helvetica, Verdana, Arial, sans-serif;
font-size: 16px;
font-weight: 300;
}
#timeline h1 {
text-align: center;
}
.progress {
position: relative;
display: block;
height: 2px;
margin: 0 auto;
width: 70%;
}
.progress div {
position: absolute;
top: 0;
display: block;
height: 2px;
}
.progress .background {
background-color: #c7c7c7;
width: 100%;
}
.progress .line {
background-color: #5282d2;
z-index: 999;
}
#timeline {
width: 90%;
margin: 0 auto;
}
#timeline ul {
list-style: none;
margin: 10px 0;
padding: 0;
}
#timeline li {
display: block;
position: relative;
background-color: #FEFEFE;
border: 2px solid #FEFEFE;
margin: 0 auto 10px;
padding: 6px;
padding-bottom: 5px;
box-shadow: 0px 1px 2px rgba(34, 25, 25, 0.4);
}
.avatar {
float: left;
max-width: 48px;
max-height: 48px;
margin-bottom: 5px;
}
.headline {
float: left;
margin-left: 8px;
}
.user {
color: #000;
font-size: 120%;
font-weight: bold;
}
.small,
.time {
font-size: 80%;
color: #777;
}
.time {
margin-bottom: 5px;
font-size: small;
}
.clearfix {
clear: both;
}
JavaScript
Vue.component('repo', {
template: '<a :href="\'https://github.com/\' + name">{{ name }}</a>',
props: ['name']
});
var vm = new Vue({
el: '#timeline',
data: {
events: null,
timeMax: 60,
time: 0,
timer: null
},
created: function() {
this.fetchData();
},
filters: {
formatDate: function(v) {
return v.replace(/T|Z/g, ' ');
}
},
methods: {
fetchData: function() {
var xhr = new XMLHttpRequest();
var self = this;
xhr.open('GET', 'https://api.github.com/events');
xhr.onload = function() {
self.events = JSON.parse(xhr.responseText);
self.timeMax = xhr.getResponseHeader("X-Poll-Interval");
self.startTimer();
};
xhr.send();
},
startTimer: function() {
var self = this;
this.time = 0;
this.timer = setInterval(function() {
self.time++;
if (self.time >= self.timeMax) {
clearInterval(self.timer);
self.fetchData();
}
}, 1000);
}
}
});