Add movie list with react

This commit is contained in:
Grégoire Delattre 2016-11-12 19:25:10 +01:00
parent 4e0faabf06
commit b20400b30f
7 changed files with 194 additions and 39 deletions

View File

@ -6,6 +6,7 @@ import webpack from 'webpack-stream';
import webpackConfig from './webpack.config.babel'; import webpackConfig from './webpack.config.babel';
const paths = { const paths = {
allSrcJs: 'src/**/*.js?(x)',
jsSrc: 'src/public/js/app.js', jsSrc: 'src/public/js/app.js',
jsDistDir: 'build/public/js', jsDistDir: 'build/public/js',
lessSrc: 'src/public/less/app.less', lessSrc: 'src/public/less/app.less',
@ -17,8 +18,8 @@ const paths = {
fontDest: 'build/public/fonts', fontDest: 'build/public/fonts',
imgSrc: 'src/public/img/*', imgSrc: 'src/public/img/*',
imgDest: 'build/public/img/', imgDest: 'build/public/img/',
goTemplatesSrc: 'src/templates/**/*.tmpl', htmlSrc: 'src/public/index.html',
goTemplatesDest: 'build/templates/', htmlDest: 'build/public/',
gulpFile: 'gulpfile.babel.js', gulpFile: 'gulpfile.babel.js',
webpackFile: 'webpack.config.babel.js', webpackFile: 'webpack.config.babel.js',
}; };
@ -41,23 +42,24 @@ gulp.task('images', () =>
.pipe(gulp.dest(paths.imgDest)) .pipe(gulp.dest(paths.imgDest))
); );
gulp.task('go-templates', () => gulp.task('html', () =>
gulp.src(paths.goTemplatesSrc) gulp.src(paths.htmlSrc)
.pipe(gulp.dest(paths.goTemplatesDest)) .pipe(gulp.dest(paths.htmlDest))
); );
gulp.task('main', ['less', 'fonts', 'images'], () => gulp.task('js', () =>
gulp.src(paths.jsSrc) gulp.src(paths.jsSrc)
.pipe(webpack(webpackConfig)) .pipe(webpack(webpackConfig))
.pipe(gulp.dest(paths.jsDistDir)) .pipe(gulp.dest(paths.jsDistDir))
); );
gulp.task('main', ['less', 'fonts', 'images', 'html', 'js'])
gulp.task('watch', () => { gulp.task('watch', () => {
gulp.watch(paths.jsSrc, ['main']); gulp.watch(paths.allSrcJs, ['js']);
gulp.watch(paths.gulpFile, ['main']);
gulp.watch(paths.lessSrc, ['less']); gulp.watch(paths.lessSrc, ['less']);
gulp.watch(paths.imgSrc, ['images']); gulp.watch(paths.imgSrc, ['images']);
gulp.watch(paths.goTemplatesSrc, ['go-templates']); gulp.watch(paths.htmlSrc, ['html']);
}); });
gulp.task('default', ['watch', 'main']); gulp.task('default', ['watch', 'main']);

View File

@ -18,6 +18,7 @@
"react-dom": "^15.3.2" "react-dom": "^15.3.2"
}, },
"devDependencies": { "devDependencies": {
"axios": "^0.15.2",
"babel": "^6.5.2", "babel": "^6.5.2",
"babel-loader": "^6.2.7", "babel-loader": "^6.2.7",
"babel-preset-latest": "^6.16.0", "babel-preset-latest": "^6.16.0",

16
src/public/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Canape</title>
<link href="/css/app.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script src="/js/app.js"></script>
</body>
</html>

View File

@ -1,32 +1,26 @@
require('jquery'); import React from 'react'
import ReactDOM from 'react-dom'
import NavBar from './navbar.jsx'
import MovieList from './movie-list.jsx'
if($('#movie-library').length >0 ){ class App extends React.Component {
listSelector(); render() {
return (
<div>
<div className="navbar navbar-inverse navbar-fixed-top">
<NavBar />
</div>
<div className="container-fluid">
<div className="container">
<MovieList />
</div>
</div>
</div>
);
}
} }
// Help select elements form the list views ReactDOM.render(
function listSelector() { <App />,
let $first = $(".thumbnail").first(); document.getElementById('app')
$first.addClass('thumbnail-selected'); );
// Get the detail
var $detail = $("#"+$first.data("imdbid")+"-detail" );
// Show it
$detail.removeClass("hidden");
$detail.addClass("show");
$(".thumbnail").click(function(e) {
e.preventDefault();
// Hide previous details
$(".movie-detail.show" ).addClass("hidden").removeClass("show");
// Change border on selected item
$('.thumbnail-selected').removeClass('thumbnail-selected');
$(this).addClass('thumbnail-selected');
// Get the detail
var $detail = $("#"+$(this).data("imdbid")+"-detail" );
// Show it
$detail.removeClass("hidden").addClass("show");
});
}

View File

@ -0,0 +1,107 @@
import React from 'react'
import axios from 'axios'
function MoviePosters(props) {
const movies = props.movies;
return (
<div className="col-xs-5 col-md-8">
<div className="row">
{movies.map((movie, index) =>
<MoviePoster
data={movie}
key={movie.ID}
onClick={ (e) => props.onClick(e, index) }
/>
)}
</div>
</div>
);
}
function MoviePoster(props) {
const imgUrl = '/img/movies/' + props.data.imdb_id +'.jpg';
const selected = props.data.selected ? ' thumbnail-selected' : '';
const imgClass = 'thumbnail' + selected;
return (
<div className="col-xs-12 col-md-3">
<a className={imgClass}>
<img src={imgUrl} onClick={props.onClick}/>
</a>
</div>
);
}
function MovieDetails(props) {
return (
<div className="col-xs-7 col-md-4">
<div className="movie-detail affix">
<h1 className="hidden-xs">{props.data.title}</h1>
<h3 className="visible-xs">{props.data.title}</h3>
<p>
<i className="fa fa-clock-o"></i>
{props.data.runtime} min
</p>
<p>
<i className="fa fa-star-o"></i>
{props.data.rating} <small>({props.data.votes} counts)</small>
</p>
<p className="movie-plot">{props.data.plot}</p>
</div>
</div>
);
}
export default class MovieList extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: [],
currentMovie: {},
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e,i) {
e.preventDefault();
let movies = this.state.movies.slice();
this.setMovies(movies, i);
}
setMovies(movies, index) {
let currentMovie = {};
movies.map(function(movie) {
movie.selected = false;
return {movie: movie};
});
if (!index && movies.length) {
index = 0;
currentMovie = movies[0];
}
if (index !== null) {
currentMovie = movies[index];
movies[index].selected = true;
}
this.setState({
movies: movies,
currentMovie: currentMovie,
});
}
componentDidMount() {
var _this = this;
this.serverRequest =
axios
.get("/movies/explore/popular")
.then(function(result) {
_this.setMovies(result.data.Data.movies);
})
}
componentWillUnmount() {
this.serverRequest.abort();
}
render() {
return (
<div className="row" id="movie-library">
<MoviePosters movies={this.state.movies} onClick={this.handleClick}/>
<MovieDetails data={this.state.currentMovie} />
</div>
);
}
}

22
src/public/js/navbar.jsx Normal file
View File

@ -0,0 +1,22 @@
import React from 'react'
export default class NavBar extends React.Component {
render() {
return (
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand" href="#">Canape</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav"></ul>
</div>
</div>
);
}
}

View File

@ -159,6 +159,12 @@ aws4@^1.2.1:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755"
axios:
version "0.15.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.2.tgz#496f50980b2ce1ad2e195af93c2d03b4d035e90d"
dependencies:
follow-redirects "0.0.7"
babel-code-frame@^6.16.0: babel-code-frame@^6.16.0:
version "6.16.0" version "6.16.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de"
@ -1207,6 +1213,13 @@ flagged-respawn@^0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5"
follow-redirects@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-0.0.7.tgz#34b90bab2a911aa347571da90f22bd36ecd8a919"
dependencies:
debug "^2.2.0"
stream-consume "^0.1.0"
font-awesome@^4.7.0: font-awesome@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
@ -2827,7 +2840,7 @@ stream-browserify@^1.0.0:
inherits "~2.0.1" inherits "~2.0.1"
readable-stream "^1.0.27-1" readable-stream "^1.0.27-1"
stream-consume@~0.1.0: stream-consume@^0.1.0, stream-consume@~0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f"