Merge pull request #7 from rschamp/login

Add log in component and basic functionality for logging in
This commit is contained in:
Ray Schamp 2015-09-16 10:33:25 -04:00
commit df1c214a01
20 changed files with 390 additions and 71 deletions

View file

@ -3,12 +3,14 @@
"curly": [2, "multi-line"],
"eol-last": [2],
"indent": [2, 4],
"quotes": [2, "single"],
"linebreak-style": [2, "unix"],
"max-len": [2, 120, 4],
"no-trailing-spaces": [2, { "skipBlankLines": true }],
"no-unused-vars": [2, {"args": "after-used", "varsIgnorePattern": "^_"}],
"quotes": [2, "single"],
"semi": [2, "always"],
"strict": [2, "never"],
"no-unused-vars": [2, {"args": "after-used", "varsIgnorePattern": "^_"}]
"space-before-function-paren": [2, "always"],
"strict": [2, "never"]
},
"env": {
"browser": true,

View file

@ -29,6 +29,7 @@
},
"devDependencies": {
"autoprefixer-loader": "2.1.0",
"classnames": "2.1.3",
"css-loader": "0.17.0",
"eslint": "1.3.1",
"eslint-plugin-react": "3.3.1",
@ -38,6 +39,7 @@
"node-sass": "3.3.2",
"react": "0.13.3",
"react-modal": "0.3.0",
"react-onclickoutside": "0.3.1",
"react-slick": "0.7.0",
"routes-to-nginx-conf": "0.0.2",
"sass-loader": "2.0.1",

View file

@ -27,7 +27,7 @@ module.exports = React.createClass({
render: function () {
return (
<Slider className={'carousel ' + this.props.className} {... this.props.settings}>
{this.props.items.map(function(item) {
{this.props.items.map(function (item) {
return (
<Thumbnail key={item.id}
href={item.href}

View file

@ -9,7 +9,7 @@ module.exports = React.createClass({
propTypes: {
projectCount: React.PropTypes.number
},
getDefaultProps: function() {
getDefaultProps: function () {
return {
projectCount: 10569070
};
@ -27,65 +27,65 @@ module.exports = React.createClass({
},
render: function () {
return (
<div className='intro'>
<div className="intro">
<div className="content">
<h1>
Create stories, games, and animations<br />
Share with others around the world
</h1>
<div className="sprites">
<a className='sprite sprite-1' href='/projects/editor/?tip_bar=getStarted'>
<a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
<img
className='costume costume-1'
src='//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png' />
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png" />
<img
className='costume costume-2'
src='//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png' />
<div className='circle'></div>
<div className='text'>TRY IT OUT</div>
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png" />
<div className="circle"></div>
<div className="text">TRY IT OUT</div>
</a>
<a className='sprite sprite-2' href='/starter_projects/'>
<a className="sprite sprite-2" href="/starter_projects/">
<img
className='costume costume-1'
src='//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png' />
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png" />
<img
className='costume costume-2'
src='//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png' />
<div className='circle'></div>
<div className='text'>SEE EXAMPLES</div>
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png" />
<div className="circle"></div>
<div className="text">SEE EXAMPLES</div>
</a>
<a className='sprite sprite-3' href='#'>
<a className="sprite sprite-3" href="#">
<img
className='costume costume-1'
src='//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png' />
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png" />
<img
className='costume costume-2'
src='//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png' />
<div className='circle'></div>
<div className='text'>JOIN SCRATCH</div>
<div className='text subtext'>( it&rsquo;s free )</div>
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png" />
<div className="circle"></div>
<div className="text">JOIN SCRATCH</div>
<div className="text subtext">( it&rsquo;s free )</div>
</a>
</div>
<div className='description'>
<div className="description">
A creative learning community with
<span className='project-count'> {this.props.projectCount.toLocaleString()} </span>
<span className="project-count"> {this.props.projectCount.toLocaleString()} </span>
projects shared
</div>
<div className='links'>
<a href='/about/'>ABOUT SCRATCH</a>
<a href='/educators/'>FOR EDUCATORS</a>
<a className='last' href='/parents/'>FOR PARENTS</a>
<div className="links">
<a href="/about/">ABOUT SCRATCH</a>
<a href="/educators/">FOR EDUCATORS</a>
<a className="last" href="/parents/">FOR PARENTS</a>
</div>
</div>
<div className='video'>
<div className='play-button' onClick={this.showVideo}></div>
<img src='//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png' />
<div className="video">
<div className="play-button" onClick={this.showVideo}></div>
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png" />
</div>
<Modal
className='video-modal'
className="video-modal"
isOpen={this.state.videoOpen}
onRequestClose={this.closeVideo}>
<iframe src='//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0' />
<iframe src="//player.vimeo.com/video/65583694?title=0&amp;byline=0&amp;portrait=0" />
</Modal>
</div>
);

View file

@ -128,9 +128,8 @@
border-right: 1px solid #000;
padding: 0 5px;
&:last-child {
border-right: 0;
}
&:last-child { border-right: 0; }
&:first-child { padding-left: 0; }
}
}

View file

@ -0,0 +1,27 @@
var React = require('react');
require('./login.scss');
module.exports = React.createClass({
propTypes: {
onLogIn: React.PropTypes.func
},
handleSubmit: function (event) {
event.preventDefault();
this.props.onLogIn();
},
render: function () {
return (
<div className="login">
<form onSubmit={this.handleSubmit}>
<label htmlFor="username">Username</label>
<input type="text" name="username" maxLength="30" />
<label htmlFor="password">Password</label>
<input type="password" name="password" />
<button className="submit-button" type="submit">Sign in</button>
<a href="/accounts/password_reset/">Forgot password?</a>
</form>
</div>
);
}
});

View file

@ -0,0 +1,7 @@
.login {
padding: 14px 9px;
.submit-button {
margin-right: 3px;
}
}

View file

@ -8,13 +8,13 @@ module.exports = React.createClass({
statics: {
setAppElement: Modal.setAppElement
},
requestClose: function() {
requestClose: function () {
return this.refs.modal.portal.requestClose();
},
render: function() {
render: function () {
return (
<Modal ref='modal' {... this.props}>
<div className='modal-close' onClick={this.requestClose}></div>
<Modal ref="modal" {... this.props}>
<div className="modal-close" onClick={this.requestClose}></div>
{this.props.children}
</Modal>
);

View file

@ -0,0 +1,3 @@
$base-background-color: #0f8bc0;
$active-background-color: rgb(1, 96, 135);
$border-color: rgb(20, 154, 203);

View file

@ -0,0 +1,37 @@
var React = require('react');
var classNames = require('classnames');
require('./dropdown.scss');
module.exports = React.createClass({
mixins: [
require('react-onclickoutside')
],
propTypes: {
onRequestClose: React.PropTypes.func,
isOpen: React.PropTypes.bool
},
getDefaultProps: function () {
return {
as: 'div',
isOpen: false
};
},
handleClickOutside: function () {
if (this.props.isOpen) {
this.props.onRequestClose();
}
},
render: function () {
var classes = classNames(
'dropdown',
this.props.className,
{open: this.props.isOpen}
);
return (
<this.props.as className={classes}>
{this.props.children}
</this.props.as>
);
}
});

View file

@ -0,0 +1,74 @@
@import 'colors';
.dropdown {
position: absolute;
right: 0;
min-width: 160px;
max-width: 220px;
background-color: $base-background-color;
overflow: hidden;
border-radius: 0px 0px 4px 4px;
box-shadow: inset 0 1px 1px rgba(100,100,100,.25),0 1px 1px rgba(0,0,0,.25);
color: white;
font-weight: normal;
font-size: 0.8125rem;
display: none;
&.open {
display: block;
}
a {
color: white;
}
input {
// 100% minus border and padding
width: calc(100% - 2px - 8px);
margin-bottom: 9px;
}
label {
display: block;
margin-bottom: 5px;
}
> li {
display: block;
line-height: 30px;
&.divider {
border-top: 1px solid #149acb;
margin-top: 10px;
}
a {
display: block;
padding: 0 10px;
&:hover {
background-color: $active-background-color;
text-decoration: none;
}
}
}
&.with-arrow {
$arrow-border-width: 11px;
overflow: visible;
margin-top: $arrow-border-width;
border-radius: 4px;
&:before {
position: absolute;
display: block;
right: 10%;
top: -$arrow-border-width;
left: auto;
border-color: transparent;
border-bottom-color: $base-background-color;
border-style: solid;
border-width: 0 $arrow-border-width $arrow-border-width $arrow-border-width;
content: " ";
}
}
}

View file

@ -1,11 +1,48 @@
var React = require('react');
var classNames = require('classnames');
var Login = require('../login/login.jsx');
var Dropdown = require('./dropdown.jsx');
require('./navigation.scss');
module.exports = React.createClass({
getInitialState: function () {
return {
'loginOpen': false,
'loggedIn': false,
'loggedInUser': {
'username': 'raimondious',
'thumbnail': '//cdn2.scratch.mit.edu/get_image/user/2584924_32x32.png'
},
'accountNavOpen': false
};
},
handleLoginClick: function (e) {
e.preventDefault();
this.setState({'loginOpen': true});
},
closeLogin: function () {
this.setState({'loginOpen': false});
},
handleLogIn: function () {
this.setState({'loggedIn': true});
},
handleLogOut: function () {
this.setState({'loggedIn': false});
},
handleClickAccountNav: function () {
this.setState({'accountNavOpen': true});
},
closeAccountNav: function () {
this.setState({'accountNavOpen': false});
},
render: function () {
var classes = classNames({
'inner': true,
'logged-in': this.state.loggedIn
});
return (
<div className="inner">
<div className={classes}>
<ul>
<li className="logo"><a href="/"></a></li>
@ -23,9 +60,38 @@ module.exports = React.createClass({
<input type="hidden" name="sort_by" value="datetime_shared" />
</form>
</li>
<li className="link right"><a href="/join">Join Scratch</a></li>
<li className="link right"><a href="">Sign In</a></li>
{this.state.loggedIn ? [
<li className="link right messages"><a href="/messages/" title="Messages">Messages</a></li>,
<li className="link right mystuff"><a href="/mystuff/" title="My Stuff">My Stuff</a></li>,
<li className="link right account-nav">
<a href="#" onClick={this.handleClickAccountNav}>
<img src={this.state.loggedInUser.thumbnail} />
{this.state.loggedInUser.username}
</a>
<Dropdown
as="ul"
isOpen={this.state.accountNavOpen}
onRequestClose={this.closeAccountNav}>
<li><a href="/users/raimondious/">Profile</a></li>
<li><a href="/mystuff/">My Stuff</a></li>
<li><a href="/accounts/settings/">Account settings</a></li>
<li className="divider">
<a href="#" onClick={this.handleLogOut}>Sign out</a>
</li>
</Dropdown>
</li>
] : [
<li className="link right join"><a href="/join">Join Scratch</a></li>,
<li className="link right">
<a href="#" onClick={this.handleLoginClick}>Sign In</a>
<Dropdown
className="login-dropdown with-arrow"
isOpen={this.state.loginOpen}
onRequestClose={this.closeLogin}>
<Login onLogIn={this.handleLogIn} />
</Dropdown>
</li>
]}
</ul>
</div>
);

View file

@ -1,3 +1,5 @@
@import 'colors';
#navigation {
position: fixed;
z-index: 10;
@ -5,12 +7,12 @@
top: 0;
left: 0;
width: 100%;
background-color: #0f8bc0;
background-color: $base-background-color;
/* NOTE: Height should match offset settings in main.scss file */
height: 35px;
ul {
.inner > ul {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
@ -21,12 +23,13 @@
padding: 0;
list-style: none;
li {
> li {
display: inline-block;
align-self: flex-start;
float: left;
height: 100%;
border-left: 1px solid rgb(20, 154, 203);
border-left: 1px solid $border-color;
position: relative;
}
.logo {
@ -49,7 +52,7 @@
}
.link {
a {
> a {
display: block;
height: 28px;
padding: 7px 15px 0 15px;
@ -60,8 +63,8 @@
white-space: nowrap;
}
a:hover {
background-color: rgb(1, 96, 135);
> a:hover {
background-color: $active-background-color;
}
}
@ -75,7 +78,7 @@
input {
display: inline-block;
height: 20px;
height: 14px;
outline: none;
border: none;
}
@ -86,7 +89,7 @@
height: 22px;
background-color: white;
background-image: url(/images/nav-search-glass.png);
background-image: url('/images/nav-search-glass.png');
background-repeat: no-repeat;
background-position: center center;
border-right: 1px solid #efefef;
@ -111,13 +114,77 @@
margin-left: auto;
font-weight: bold;
a:hover {
background-color: #f79231;
}
&:last-child {
border-right: 1px solid rgb(20, 154, 203);
}
}
.join > a:hover {
background-color: #f79231;
}
.messages, .mystuff {
> a {
background-repeat: no-repeat;
background-position: center center;
padding-left: 10px;
padding-right: 10px;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
}
}
.messages {
> a {
background-image: url('/images/nav-notifications.png');
width: 22px;
}
}
.mystuff {
> a {
background-image: url('/images/mystuff.png');
width: 25px;
}
}
.login-dropdown {
width: 200px;
}
.account-nav {
> a {
font-weight: normal;
font-size: 0.8125rem;
img {
width: 24px;
height: 24px;
margin-right: 5px;
vertical-align: middle;
}
&:after {
$caret-border-width: 4px;
margin-left: $caret-border-width;
border: $caret-border-width solid transparent;
border-bottom-width: 0;
border-top-color: white;
content: " ";
opacity: 0.5;
vertical-align: middle;
width: 0;
height: 0;
display: inline-block;
}
}
.dropdown {
width: 100%;
padding: 0;
padding-top: 5px;
}
}
}
}

View file

@ -15,9 +15,9 @@ module.exports = React.createClass({
},
render: function () {
return (
<Box
className="news"
title="Scratch News"
<Box
className="news"
title="Scratch News"
moreTitle="View All"
moreHref="/news">

View file

@ -17,11 +17,11 @@ module.exports = React.createClass({
render: function () {
return (
<div className={'thumbnail ' + this.props.className}>
<a className='thumbnail-image' href={this.props.href}>
<a className="thumbnail-image" href={this.props.href}>
<img src={this.props.src} />
</a>
<span className='thumbnail-title'><a href={this.props.href}>{this.props.title}</a></span>
<span className='thumbnail-extra'>{this.props.extra}</span>
<span className="thumbnail-title"><a href={this.props.href}>{this.props.title}</a></span>
<span className="thumbnail-extra">{this.props.extra}</span>
</div>
);
}

View file

@ -53,3 +53,38 @@ a:hover {
/* NOTE: Margin should match height in navigation.scss */
margin-top: 35px;
}
/* Forms */
input {
height: 18px;
line-height: 18px;
display: inline-block;
padding: 4px;
margin-bottom: 9px;
font-size: 13px;
color: #555;
border: 1px solid #ccc;
border-radius: 3px;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
transition: border linear .2s,box-shadow linear .2s;
}
button {
cursor: pointer;
line-height: 30px;
border: 1px solid #eee;
border-radius: 5px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
transition: all .25s ease-in-out;
height: 32px;
text-shadow: 0 1px #fff;
font-weight: normal;
color: #666;
border-color: #999;
display: inline-block;
background: linear-gradient(to bottom, #fff, #ccc)
}
button:hover {
background-image: linear-gradient(#e6e6e6,#e6e6e6);
}

View file

@ -10,7 +10,7 @@ var View = React.createClass({
return (
<div className="inner">
<h1>Box Component</h1>
<Box
<Box
title="Some Title"
more="Cat Gifs"
moreUrl="http://www.catgifpage.com/">

View file

@ -34,7 +34,7 @@ var View = React.createClass({
<Intro projectCount={this.state.projectCount} />
<div className="activity"></div>
<News />
{this.state.featured.map(function(set){
{this.state.featured.map(function (set) {
return (
<Box
className="featured"

BIN
static/images/mystuff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB