Add movie list with react
This commit is contained in:
parent
4e0faabf06
commit
b20400b30f
@ -6,6 +6,7 @@ import webpack from 'webpack-stream';
|
||||
import webpackConfig from './webpack.config.babel';
|
||||
|
||||
const paths = {
|
||||
allSrcJs: 'src/**/*.js?(x)',
|
||||
jsSrc: 'src/public/js/app.js',
|
||||
jsDistDir: 'build/public/js',
|
||||
lessSrc: 'src/public/less/app.less',
|
||||
@ -17,8 +18,8 @@ const paths = {
|
||||
fontDest: 'build/public/fonts',
|
||||
imgSrc: 'src/public/img/*',
|
||||
imgDest: 'build/public/img/',
|
||||
goTemplatesSrc: 'src/templates/**/*.tmpl',
|
||||
goTemplatesDest: 'build/templates/',
|
||||
htmlSrc: 'src/public/index.html',
|
||||
htmlDest: 'build/public/',
|
||||
gulpFile: 'gulpfile.babel.js',
|
||||
webpackFile: 'webpack.config.babel.js',
|
||||
};
|
||||
@ -41,23 +42,24 @@ gulp.task('images', () =>
|
||||
.pipe(gulp.dest(paths.imgDest))
|
||||
);
|
||||
|
||||
gulp.task('go-templates', () =>
|
||||
gulp.src(paths.goTemplatesSrc)
|
||||
.pipe(gulp.dest(paths.goTemplatesDest))
|
||||
gulp.task('html', () =>
|
||||
gulp.src(paths.htmlSrc)
|
||||
.pipe(gulp.dest(paths.htmlDest))
|
||||
);
|
||||
|
||||
gulp.task('main', ['less', 'fonts', 'images'], () =>
|
||||
gulp.task('js', () =>
|
||||
gulp.src(paths.jsSrc)
|
||||
.pipe(webpack(webpackConfig))
|
||||
.pipe(gulp.dest(paths.jsDistDir))
|
||||
);
|
||||
|
||||
gulp.task('main', ['less', 'fonts', 'images', 'html', 'js'])
|
||||
|
||||
gulp.task('watch', () => {
|
||||
gulp.watch(paths.jsSrc, ['main']);
|
||||
gulp.watch(paths.gulpFile, ['main']);
|
||||
gulp.watch(paths.allSrcJs, ['js']);
|
||||
gulp.watch(paths.lessSrc, ['less']);
|
||||
gulp.watch(paths.imgSrc, ['images']);
|
||||
gulp.watch(paths.goTemplatesSrc, ['go-templates']);
|
||||
gulp.watch(paths.htmlSrc, ['html']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['watch', 'main']);
|
||||
|
@ -18,6 +18,7 @@
|
||||
"react-dom": "^15.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.15.2",
|
||||
"babel": "^6.5.2",
|
||||
"babel-loader": "^6.2.7",
|
||||
"babel-preset-latest": "^6.16.0",
|
||||
|
16
src/public/index.html
Normal file
16
src/public/index.html
Normal 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>
|
@ -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 ){
|
||||
listSelector();
|
||||
class App extends React.Component {
|
||||
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
|
||||
function listSelector() {
|
||||
let $first = $(".thumbnail").first();
|
||||
$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");
|
||||
});
|
||||
}
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('app')
|
||||
);
|
||||
|
107
src/public/js/movie-list.jsx
Normal file
107
src/public/js/movie-list.jsx
Normal 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
22
src/public/js/navbar.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
15
yarn.lock
15
yarn.lock
@ -159,6 +159,12 @@ aws4@^1.2.1:
|
||||
version "1.5.0"
|
||||
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:
|
||||
version "6.16.0"
|
||||
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"
|
||||
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:
|
||||
version "4.7.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user