A Pen by Sean Rigney on CodePen.
Last active
August 25, 2016 15:03
-
-
Save seanr707/792b5e7208e0511496b9a65a82f11d15 to your computer and use it in GitHub Desktop.
Camper Leaderboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Sean's FreeCodeCamp Leaderboard in React --> | |
#react-root |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const Leaderboard = React.createClass({ | |
// Set up array property to store data from _ajax | |
storedData: [], | |
getInitialState: function() { | |
return { | |
'recent': true, | |
'ready': false | |
}; | |
}, | |
componentDidMount: function() { | |
// preload data on mount | |
this._ajax(this.props.recent); | |
this._ajax(this.props.alltime); | |
}, | |
_ajax: function(url) { | |
console.log('Fetching new data...'); | |
const callback = (res) => { | |
// Adds array received from FreeCodeCamp API | |
this.storedData = this.storedData.concat(res.data); | |
// Sets ready to true to render first data (recent) | |
this.setState({'ready': true}); | |
} | |
axios.get(url) | |
.then(callback) | |
.catch(err => { | |
console.log(err); | |
}); | |
}, | |
handleRecent: function () { | |
// Do nothing if Recent is already active | |
if (this.state.recent) return; | |
this.setState({'recent': !this.state.recent}); | |
}, | |
handleAlltime: function () { | |
if (!this.state.recent) return; | |
this.setState({'recent': !this.state.recent}); | |
}, | |
render: function () { | |
let recentHeader: string = 'Recent'; | |
let alltimeHeader: string = 'All'; | |
// Adds down arrow next to active sorting header | |
if (this.state.recent) { | |
recentHeader += '\u25BE'; | |
} else { | |
alltimeHeader += '\u25BE'; | |
} | |
// Contains main table and navbar (for use on small screens and mobile) | |
return ( | |
<div id="base"> | |
<nav id="mobileNav" className="nav navbar navbar-default navbar-fixed-top"> | |
<div className="container"> | |
<div className="row"> | |
<div className={this.props.columns.position}>#</div> | |
<div className={this.props.columns.username}>Username</div> | |
<div className={this.props.columns.points} onClick={this.handleRecent}>{recentHeader}</div> | |
<div className={this.props.columns.points} onClick={this.handleAlltime}>{alltimeHeader}</div> | |
</div> | |
</div> | |
</nav> | |
<table id="mainTable" className="container"> | |
<thead className="container"> | |
<tr id="headers" className="row"> | |
<th scope="col" id="leftCorner" className={this.props.columns.position}>#</th> | |
<th scope="col" className={this.props.columns.username}>Username</th> | |
<th scope="col" className={this.props.columns.points} onClick={this.handleRecent}>{recentHeader}</th> | |
<th scope="col" id="rightCorner" className={this.props.columns.points} onClick={this.handleAlltime}>{alltimeHeader}</th> | |
</tr> | |
</thead> | |
<Users data={this.storedData} sorting={this.state.recent ? 'recent' : 'alltime'} columns={this.props.columns} />; | |
</table> | |
</div> | |
); | |
} | |
}); | |
const Users = React.createClass({ | |
// Takes array and outputs top 25 user of designated sorting method | |
_sortArray: function (array, sorting) { | |
let tempObj = {}; | |
// Populates temp object with the number of points of users as keys | |
array.forEach((data) => { | |
tempObj[data[sorting]] = data; | |
}); | |
// Takes array of points/keys and sorts them from largest to smallest | |
return Object.keys(tempObj).sort((a, b) => { | |
// Sort numerically | |
return (a - b); | |
}).reverse().map((key, i) => { | |
// Add position property used for display and key(React) | |
tempObj[key]['position'] = i + 1; | |
// Places object into an array | |
return tempObj[key]; | |
}).splice(0, 25).map(data => { | |
// returns top 25 users (of set brownie points) | |
return ( | |
<User | |
key={data.position} | |
username={data.username} | |
avatar={data.img} | |
recent={data.recent} | |
alltime={data.alltime} | |
position={data.position} | |
columns={this.props.columns} | |
/> | |
); | |
}); | |
}, | |
// Renders 'Loading...' until data array is filled. Then populates table with User components | |
render: function () { | |
if (this.props.data.length === 0) return (<tbody><tr><td>Loading...</td></tr></tbody>); | |
let users = this._sortArray(this.props.data, this.props.sorting); | |
return ( | |
<tbody className="container"> | |
{users} | |
</tbody> | |
); | |
} | |
}); | |
const User = React.createClass({ | |
// Renders each row for a user in the table | |
render: function () { | |
const userLink = "https://freecodecamp.com/" + this.props.username; | |
return ( | |
<tr className="row"> | |
<td className={this.props.columns.position}> | |
{this.props.position} | |
</td> | |
<td className={this.props.columns.username}> | |
<a href={userLink}> | |
<img alt="avatar" src={this.props.avatar} /> | |
<p className="username">{this.props.username}</p> | |
</a> | |
</td> | |
<td className={this.props.columns.points}> | |
{this.props.recent} | |
</td> | |
<td className={this.props.columns.points}> | |
{this.props.alltime} | |
</td> | |
</tr> | |
); | |
} | |
}); | |
(function () { | |
const RECENT = 'https://fcctop100.herokuapp.com/api/fccusers/top/recent'; | |
const ALLTIME = 'https://fcctop100.herokuapp.com/api/fccusers/top/alltime'; | |
// Bootstrap widths for Columns of table | |
const columns = { | |
position: 'col-md-1 col-sm-1 col-xs-1 positionClass', | |
username: 'col-md-5 col-sm-5 col-xs-7 usernameClass', | |
points: 'col-md-3 col-sm-3 col-xs-2 pointsClass' | |
} | |
ReactDOM.render(<Leaderboard recent={RECENT} allTime={ALLTIME} columns={columns} />, document.getElementById('react-root')); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://fb.me/react-15.1.0.js"></script> | |
<script src="https://fb.me/react-dom-15.1.0.js"></script> | |
<script src="https://npmcdn.com/axios/dist/axios.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$maxScreen: 800px; | |
body { | |
// Picture of cartoon brownies | |
background-image: url("http://static.vecteezy.com/system/resources/previews/000/101/295/original/brownie-vectors.jpg"); | |
background-attachment: fixed; | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: cover; | |
} | |
a { | |
text-decoration: none; | |
color: #555; | |
&:hover { | |
text-decoration: none; | |
} | |
} | |
img { | |
width: 32px; | |
margin: 10px 0 10px 0; | |
// Decreases margins to allow for less scrolling on smaller screens | |
@media screen and (max-width: $maxScreen) { | |
// visibility: hidden; | |
// width: 24px; | |
margin: 5px 0 5px 0; | |
} | |
} | |
.username { | |
// Keeps username inline with avatar | |
display: inline; | |
margin: 0 0 0 10px; | |
} | |
.positionClass { | |
text-align: right; | |
// padding: 5px; | |
} | |
.pointsClass { | |
text-align: center; | |
} | |
// Mobile navbar only to be shown on small screens | |
#mobileNav { | |
visibility: hidden; | |
@media screen and (max-width: $maxScreen) { | |
visibility: visible; | |
background: purple; | |
color: white; | |
// Padding to center text vertically | |
padding-top: 15px; | |
box-shadow: 0 1px 2px #777; | |
} | |
} | |
#mainTable { | |
$borderRadius: 5px; | |
// Gives purple glow to table | |
box-shadow: 0 1px 5px purple; | |
border-radius: $borderRadius $borderRadius 0 0; | |
background: purple; | |
opacity: .9; | |
// Gives offset for main view and mobile view | |
margin: 50px auto 50px auto; | |
transition: all 2s ease; | |
// Keeps leader small if screen is large | |
@media screen and (min-width: 1080px) { | |
width: 450px; | |
} | |
// Stretches table out to fullscreen on small screens | |
@media screen and (max-width: $maxScreen) { | |
width: 100%; | |
} | |
// Alternates colors | |
tbody { | |
tr:nth-child(even) { | |
background-color: white; | |
} | |
tr:nth-child(odd) { | |
background-color: #ededed; | |
} | |
} | |
// Data for table headers (cells) | |
th { | |
background: purple; | |
color: white; | |
padding: 5px; | |
// Gives roundness to top corner cells | |
&#leftCorner { | |
border-radius: $borderRadius 0 0 0; | |
} | |
&#rightCorner { | |
border-radius: 0 $borderRadius 0 0; | |
} | |
} | |
} | |
tr#headers { | |
// Removes weird background from messing up shadows | |
background: none; | |
// Hides header row when on mobile / small screen | |
@media screen and (max-width: $maxScreen) { | |
position: fixed; | |
top: 0; | |
visibility: hidden; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment