├── README.md ├── badge └── index.html ├── dropdown ├── .gitignore ├── application.js ├── gulpfile.js ├── index.html ├── package.json └── src │ ├── app.jsx │ ├── button.jsx │ ├── dropdown.jsx │ └── list-item.jsx ├── hello-world └── index.html ├── imgur-client ├── .gitignore ├── README.md ├── gulpfile.js ├── index.html ├── package.json ├── sass │ ├── header.scss │ ├── image-detail.scss │ ├── image-preview.scss │ ├── style.scss │ └── topic.scss └── src │ ├── actions.jsx │ ├── app.jsx │ ├── components │ ├── comment-box.jsx │ ├── header.jsx │ ├── image-detail.jsx │ ├── image-preview.jsx │ ├── main.jsx │ ├── topic-list.jsx │ └── topic.jsx │ ├── routes.jsx │ ├── stores │ ├── comment-store.jsx │ ├── image-store.jsx │ └── topic-store.jsx │ └── utils │ └── api.jsx ├── thumbnail-gulp ├── .gitignore ├── application.js ├── gulpfile.js ├── index.html ├── package.json └── src │ ├── app.jsx │ ├── badge.jsx │ ├── thumbnail-list.jsx │ └── thumbnail.jsx ├── thumbnail-list └── index.html ├── thumbnail └── index.html └── todos ├── .gitignore ├── README.md ├── application.js ├── gulpfile.js ├── index.html ├── package.json └── src ├── app.jsx ├── header.jsx ├── list-item.jsx └── list.jsx /README.md: -------------------------------------------------------------------------------- 1 | # ReactCasts 2 | 3 | This is the companion git repository for the course Build Web Apps with React JS and Flux. 4 | 5 | Each example from the course can be found within this repo. You can either look at the files 6 | in a completed state, or check out the changes that were made in a particular section 7 | by clicking on one of the links below. 8 | 9 | | Section Number | Section Name | Link to Commit | 10 | |---------------------------------------------------|------------| --- | 11 | | 1 | Introduction | | 12 | | 2 | Link to Github Repository | | 13 | | 3 | JSX Markup in Our Views | | 14 | | 4 | First Application - Basic Building Blocks | [6eac86c](https://github.com/StephenGrider/ReactCasts/commit/c5b1923) | 15 | | 5 | First Application - Creating a React Class | [8ee74dd](https://github.com/StephenGrider/ReactCasts/commit/8ee74dd) | 16 | | 6 | First Application - Showing Content | [df8b9ed](https://github.com/StephenGrider/ReactCasts/commit/df8b9ed) | 17 | | 7 | Exploring Props - Customizing Views | [d67b329](https://github.com/StephenGrider/ReactCasts/commit/d67b329) | 18 | | 8 | Exploring Props - Wiring Up Our Data | [0f49ee5](https://github.com/StephenGrider/ReactCasts/commit/0f49ee5) | 19 | | 9 | Composition - Views Within Views | [5693d3c](https://github.com/StephenGrider/ReactCasts/commit/5693d3c) | 20 | | 10 | Composition - Props Selection | | 21 | | 11 | Lists | [ee7157f](https://github.com/StephenGrider/ReactCasts/commit/ee7157f) | 22 | | 12 | Tooling - Breaking Up Our Code | [3ac4d7c](https://github.com/StephenGrider/ReactCasts/commit/3ac4d7c) | 23 | | 13 | Tooling - Applying NPM | [c3f7c77](https://github.com/StephenGrider/ReactCasts/commit/c3f7c77) | 24 | | 14 | Tooling - Exporting Code | [](https://github.com/StephenGrider/ReactCasts/commit/) | 25 | | 15 | Tooling - Gulpfile | [2112054](https://github.com/StephenGrider/ReactCasts/commit/2112054) | 26 | | 16 | Tooling - Final Refactor | [50d6b89](https://github.com/StephenGrider/ReactCasts/commit/50d6b89) | 27 | | 17 | Exploring State and Events - Purpose of State | [c8c0da4](https://github.com/StephenGrider/ReactCasts/commit/c8c0da4) | 28 | | 18 | Exploring State and Events - Bringing Button Back | [86d0dc1](https://github.com/StephenGrider/ReactCasts/commit/86d0dc1) | 29 | | 19 | Exploring State and Events - Adding Content | [b2bf2d4](https://github.com/StephenGrider/ReactCasts/commit/b2bf2d4) | 30 | | 20 | Exploring State and Events - Toggling Visibility | [3e8921a](https://github.com/StephenGrider/ReactCasts/commit/3e8921a) | 31 | | 21 | Exploring State and Events - Selecting Items | [3625a8d](https://github.com/StephenGrider/ReactCasts/commit/3625a8d) | 32 | | 22 | Firebase - Building From a New Start | | 33 | | 23 | Firebase - Signing up and Integrating Firebase | | 34 | | 24 | Building From a New Start | | 35 | | 25 | Signing up and Integrating Firebase | [2ca16df](https://github.com/StephenGrider/ReactCasts/commit/2ca16df) | 36 | | 26 | Building Our Application Architecture | [4567631](https://github.com/StephenGrider/ReactCasts/commit/4567631) | 37 | | 27 | Hooking Up Our Remote Datastore | [0233b4a](https://github.com/StephenGrider/ReactCasts/commit/0233b4a) | 38 | | 28 | Scaffolding the Header | [74f393e](https://github.com/StephenGrider/ReactCasts/commit/74f393e) | 39 | | 29 | Handling Form Input | [952235f](https://github.com/StephenGrider/ReactCasts/commit/952235f) | 40 | | 30 | Pushing Data to Firebase | [65eae7c](https://github.com/StephenGrider/ReactCasts/commit/65eae7c) | 41 | | 31 | Rendering a List of Items | [6336102](https://github.com/StephenGrider/ReactCasts/commit/6336102) | 42 | | 32 | Waiting on Data Before Rendering | [c9cf6c9](https://github.com/StephenGrider/ReactCasts/commit/c9cf6c9) | 43 | | 33 | Building Item Component | [323226d](https://github.com/StephenGrider/ReactCasts/commit/323226d) | 44 | | 34 | Updating Data to the Remote Datastore | [a466ecb](https://github.com/StephenGrider/ReactCasts/commit/a466ecb) | 45 | | 35 | Debugging Firebase Updating | [631007d](https://github.com/StephenGrider/ReactCasts/commit/631007d) | 46 | | 36 | Allowing Editing and Undoing | [f3d0640](https://github.com/StephenGrider/ReactCasts/commit/f3d0640) | 47 | | 37 | Saving Edit | [4650605](https://github.com/StephenGrider/ReactCasts/commit/4650605) | 48 | | 38 | Bulk Delete Data | [6ff8591](https://github.com/StephenGrider/ReactCasts/commit/6ff8591) | 49 | | 39 | Project Overview | | 50 | | 40 | Imgur API Overview | | 51 | | 41 | Imgur API in Detail | | 52 | | 42 | React Router Demo | [3ea1d06](https://github.com/StephenGrider/ReactCasts/commit/3ea1d06) | 53 | | 43 | Nesting Route | [f268536](https://github.com/StephenGrider/ReactCasts/commit/f268536) | 54 | | 44 | Refactor to Separate Rendering and Routing | [3403ae6](https://github.com/StephenGrider/ReactCasts/commit/3403ae6) | 55 | | 45 | In-App Navigation | [92a5b98](https://github.com/StephenGrider/ReactCasts/commit/92a5b98) | 56 | | 46 | Implementing Fetch | [0d47fe0](https://github.com/StephenGrider/ReactCasts/commit/0d47fe0) | 57 | | 47 | Working with Fetch's Promise | [cacf778](https://github.com/StephenGrider/ReactCasts/commit/cacf778) | 58 | | 48 | Display a List of Topic | [42bcfc6](https://github.com/StephenGrider/ReactCasts/commit/42bcfc6) | 59 | | 49 | Fetching Data Naively | [f6c8c35](https://github.com/StephenGrider/ReactCasts/commit/f6c8c35) | 60 | | 50 | Working With Stores | [f114d82](https://github.com/StephenGrider/ReactCasts/commit/f114d82) | 61 | | 51 | Triggering Changes From a Store | [136aa1f](https://github.com/StephenGrider/ReactCasts/commit/136aa1f) | 62 | | 52 | Working with Action | [10519ed](https://github.com/StephenGrider/ReactCasts/commit/10519ed) | 63 | | 53 | Recap of Flux Data Fetching | | 64 | | 54 | Routing with Parameters | [6c2e386](https://github.com/StephenGrider/ReactCasts/commit/6c2e386) | 65 | | 55 | Matching Parameters | [fcfe2fc](https://github.com/StephenGrider/ReactCasts/commit/fcfe2fc) | 66 | | 56 | Rendering Topics in the Header | [6680a3a](https://github.com/StephenGrider/ReactCasts/commit/6680a3a) | 67 | | 57 | React Router Helper | [b9a82f7](https://github.com/StephenGrider/ReactCasts/commit/b9a82f7) | 68 | | 58 | Implementing Image Store | [c8ea3ad](https://github.com/StephenGrider/ReactCasts/commit/c8ea3ad) | 69 | | 59 | Refetching Data on Rerender | [265e9cd](https://github.com/StephenGrider/ReactCasts/commit/265e9cd) | 70 | | 60 | Scaffolding Image Preview | [b61d75e](https://github.com/StephenGrider/ReactCasts/commit/b61d75e) | 71 | | 61 | Filtering Image Data | [56668a1](https://github.com/StephenGrider/ReactCasts/commit/56668a1) | 72 | | 62 | Playing Videos on Mouseover | [54c0f79](https://github.com/StephenGrider/ReactCasts/commit/54c0f79) | 73 | | 63 | Showing a Play Button for Each Image | [77ef623](https://github.com/StephenGrider/ReactCasts/commit/77ef623) | 74 | | 64 | Adding an Image Stats Overlay | [7c86a66](https://github.com/StephenGrider/ReactCasts/commit/7c86a66) | 75 | | 65 | Scaffolding Image Detail | [e606fad](https://github.com/StephenGrider/ReactCasts/commit/e606fad) | 76 | | 66 | Fetching Single Records from a Store | [2528ac5](https://github.com/StephenGrider/ReactCasts/commit/2528ac5) | 77 | | 67 | Fetching Single Records from a Store Continue | [b573cb7](https://github.com/StephenGrider/ReactCasts/commit/b573cb7) | 78 | | 68 | Rendering an Image Detail | [95af337](https://github.com/StephenGrider/ReactCasts/commit/95af337) | 79 | | 69 | Actions With Multiple Methods | [43e3865](https://github.com/StephenGrider/ReactCasts/commit/43e3865) | 80 | | 70 | Listening to Many Changes in a Component | [a3a6207](https://github.com/StephenGrider/ReactCasts/commit/a3a6207) | 81 | | 71 | CSS Animation | [9bfde96](https://github.com/StephenGrider/ReactCasts/commit/9bfde96) | 82 | -------------------------------------------------------------------------------- /badge/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 37 | -------------------------------------------------------------------------------- /dropdown/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | main.js 3 | -------------------------------------------------------------------------------- /dropdown/application.js: -------------------------------------------------------------------------------- 1 | var options = { 2 | thumbnailData: [{ 3 | title: 'Show Courses', 4 | number: 12, 5 | header: 'Learn React', 6 | description: 'React is a fantastic new front end library for rendering web pages. React is a fantastic new front end library for rendering web pages.', 7 | imageUrl: 'https://raw.githubusercontent.com/wiki/facebook/react/react-logo-1000-transparent.png' 8 | },{ 9 | title: 'Show Courses', 10 | number: 25, 11 | header: 'Learn Gulp', 12 | description: 'Gulp will speed up your development workflow. Gulp will speed up your development workflow. Gulp will speed up your development workflow.', 13 | imageUrl: 'http://brunch.io/images/others/gulp.png' 14 | }] 15 | }; 16 | 17 | var element = React.createElement(ThumbnailList, options); 18 | React.render(element, document.querySelector('.container')); 19 | 20 | var Badge = React.createClass({displayName: "Badge", 21 | render: function() { 22 | return React.createElement("button", {className: "btn btn-primary", type: "button"}, 23 | this.props.title, " ", React.createElement("span", {className: "badge"}, this.props.number) 24 | ) 25 | } 26 | }); 27 | 28 | var ThumbnailList = React.createClass({displayName: "ThumbnailList", 29 | render: function() { 30 | var list = this.props.thumbnailData.map(function(thumbnailProps){ 31 | return React.createElement(Thumbnail, React.__spread({}, thumbnailProps)) 32 | }); 33 | 34 | return React.createElement("div", null, 35 | list 36 | ) 37 | } 38 | }); 39 | 40 | var Thumbnail = React.createClass({displayName: "Thumbnail", 41 | render: function() { 42 | return React.createElement("div", {className: "col-sm-6 col-md-4"}, 43 | React.createElement("div", {className: "thumbnail"}, 44 | React.createElement("img", {src: this.props.imageUrl, alt: "..."}), 45 | React.createElement("div", {className: "caption"}, 46 | React.createElement("h3", null, this.props.header), 47 | React.createElement("p", null, this.props.description), 48 | React.createElement("p", null, 49 | React.createElement(Badge, {title: this.props.title, number: this.props.number}) 50 | ) 51 | ) 52 | ) 53 | ) 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /dropdown/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gutil = require('gulp-util'); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | var watchify = require('watchify'); 6 | var reactify = require('reactify'); 7 | 8 | gulp.task('default', function() { 9 | var bundler = watchify(browserify({ 10 | entries: ['./src/app.jsx'], 11 | transform: [reactify], 12 | extensions: ['.jsx'], 13 | debug: true, 14 | cache: {}, 15 | packageCache: {}, 16 | fullPaths: true 17 | })); 18 | 19 | function build(file) { 20 | if (file) gutil.log('Recompiling ' + file); 21 | return bundler 22 | .bundle() 23 | .on('error', gutil.log.bind(gutil, 'Browserify Error')) 24 | .pipe(source('main.js')) 25 | .pipe(gulp.dest('./')); 26 | }; 27 | build(); 28 | bundler.on('update', build); 29 | }); 30 | -------------------------------------------------------------------------------- /dropdown/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /dropdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thumbnail-gulp", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "browserify": "^9.0.3", 12 | "gulp": "^3.8.11", 13 | "gulp-concat": "^2.5.2", 14 | "gulp-react": "^3.0.1", 15 | "gulp-util": "^3.0.4", 16 | "react": "^0.13.1", 17 | "reactify": "^1.1.0", 18 | "vinyl-source-stream": "^1.1.0", 19 | "watchify": "^2.4.0" 20 | }, 21 | "devDependencies": {} 22 | } 23 | -------------------------------------------------------------------------------- /dropdown/src/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Dropdown = require('./dropdown'); 3 | 4 | var options = { 5 | title: 'Choose a dessert', // What should show up on the button to open/close the dropdown 6 | items: [ // List of items to show in the dropdown 7 | 'Apple Pie', 8 | 'Peach Cobbler', 9 | 'Coconut Cream Pie' 10 | ] 11 | }; 12 | 13 | var element = React.createElement(Dropdown, options); 14 | React.render(element, document.querySelector('.container')); 15 | -------------------------------------------------------------------------------- /dropdown/src/button.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | render: function() { 5 | return 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /dropdown/src/dropdown.jsx: -------------------------------------------------------------------------------- 1 | // We need to show a button and a list 2 | // This component should know when to show the list 3 | // based on when the user clicks on a button 4 | 5 | var React = require('react'); 6 | var Button = require('./button'); 7 | var ListItem = require('./list-item'); 8 | 9 | module.exports = React.createClass({ 10 | handleClick: function() { 11 | this.setState({open: !this.state.open}); 12 | }, 13 | getInitialState: function(){ 14 | return { open: false } 15 | }, 16 | handleItemClick: function(item) { 17 | this.setState({ 18 | open: false, 19 | itemTitle: item 20 | }); 21 | }, 22 | render: function() { 23 | var list = this.props.items.map(function(item){ 24 | return 29 | }.bind(this)); 30 | 31 | return
32 |
42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /dropdown/src/list-item.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | handleClick: function() { 5 | this.props.whenItemClicked(this.props.item); 6 | }, 7 | render: function() { 8 | return
  • 9 | {this.props.item} 10 |
  • 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /hello-world/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /imgur-client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | main.js 3 | style.css 4 | -------------------------------------------------------------------------------- /imgur-client/README.md: -------------------------------------------------------------------------------- 1 | ReactStarter 2 | ==== 3 | 4 | Use this as a starting point for working on chapters of the [Learn and Understand React JS](https://www.udemy.com/learn-and-understand-reactjs/) course on Udemy.com. 5 | 6 | --- 7 | 8 | ###Getting Started### 9 | 10 | There are two methods for getting started with this repo. 11 | 12 | ####Familiar with Git?##### 13 | Checkout this repo, install depdencies, then start the gulp process with the following: 14 | 15 | ``` 16 | > git clone git@github.com:StephenGrider/ReactStarter.git 17 | > cd ReactStarter 18 | > npm install 19 | > gulp 20 | ``` 21 | 22 | ####Not Familiar with Git?##### 23 | Click [here](https://github.com/StephenGrider/ReactStarter/releases) then download the .zip file. Extract the contents of the zip file, then open your terminal, change to the project directory, and: 24 | 25 | ``` 26 | > npm install 27 | > gulp 28 | ``` 29 | -------------------------------------------------------------------------------- /imgur-client/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gutil = require('gulp-util'); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | var watchify = require('watchify'); 6 | var reactify = require('reactify'); 7 | var notifier = require('node-notifier'); 8 | var server = require('gulp-server-livereload'); 9 | var concat = require('gulp-concat'); 10 | var sass = require('gulp-sass'); 11 | var watch = require('gulp-watch'); 12 | 13 | var notify = function(error) { 14 | var message = 'In: '; 15 | var title = 'Error: '; 16 | 17 | if(error.description) { 18 | title += error.description; 19 | } else if (error.message) { 20 | title += error.message; 21 | } 22 | 23 | if(error.filename) { 24 | var file = error.filename.split('/'); 25 | message += file[file.length-1]; 26 | } 27 | 28 | if(error.lineNumber) { 29 | message += '\nOn Line: ' + error.lineNumber; 30 | } 31 | 32 | notifier.notify({title: title, message: message}); 33 | }; 34 | 35 | var bundler = watchify(browserify({ 36 | entries: ['./src/app.jsx'], 37 | transform: [reactify], 38 | extensions: ['.jsx'], 39 | debug: true, 40 | cache: {}, 41 | packageCache: {}, 42 | fullPaths: true 43 | })); 44 | 45 | function bundle() { 46 | return bundler 47 | .bundle() 48 | .on('error', notify) 49 | .pipe(source('main.js')) 50 | .pipe(gulp.dest('./')) 51 | } 52 | bundler.on('update', bundle) 53 | 54 | gulp.task('build', function() { 55 | bundle() 56 | }); 57 | 58 | gulp.task('serve', function(done) { 59 | gulp.src('') 60 | .pipe(server({ 61 | livereload: { 62 | enable: true, 63 | filter: function(filePath, cb) { 64 | if(/main.js/.test(filePath)) { 65 | cb(true) 66 | } else if(/style.css/.test(filePath)){ 67 | cb(true) 68 | } 69 | } 70 | }, 71 | open: true 72 | })); 73 | }); 74 | 75 | gulp.task('sass', function () { 76 | gulp.src('./sass/**/*.scss') 77 | .pipe(sass().on('error', sass.logError)) 78 | .pipe(concat('style.css')) 79 | .pipe(gulp.dest('./')); 80 | }); 81 | 82 | gulp.task('default', ['build', 'serve', 'sass', 'watch']); 83 | 84 | gulp.task('watch', function () { 85 | gulp.watch('./sass/**/*.scss', ['sass']); 86 | }); 87 | -------------------------------------------------------------------------------- /imgur-client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | -------------------------------------------------------------------------------- /imgur-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "browserify": "^9.0.3", 12 | "gulp": "^3.8.11", 13 | "gulp-concat": "^2.5.2", 14 | "gulp-react": "^3.0.1", 15 | "gulp-sass": "^2.0.1", 16 | "gulp-server-livereload": "^1.3.0", 17 | "gulp-util": "^3.0.4", 18 | "gulp-watch": "^4.2.4", 19 | "lodash": "^3.10.0", 20 | "node-notifier": "^4.2.1", 21 | "react": "^0.13.3", 22 | "react-router": "1.0.0-beta2", 23 | "reactify": "^1.1.0", 24 | "reflux": "^0.2.8", 25 | "vinyl-source-stream": "^1.1.0", 26 | "watchify": "^2.4.0", 27 | "whatwg-fetch": "^0.9.0" 28 | }, 29 | "devDependencies": {} 30 | } 31 | -------------------------------------------------------------------------------- /imgur-client/sass/header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | .active { 3 | font-weight: 900; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /imgur-client/sass/image-detail.scss: -------------------------------------------------------------------------------- 1 | .image-detail { 2 | .panel-body { 3 | text-align: center; 4 | } 5 | 6 | img, video { 7 | max-width: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /imgur-client/sass/image-preview.scss: -------------------------------------------------------------------------------- 1 | .image-preview { 2 | display: inline-block; 3 | position: relative; 4 | 5 | .inset { 6 | position: absolute; 7 | bottom: 5px; 8 | left: 0px; 9 | width: 100%; 10 | background-color: white; 11 | text-align: center; 12 | opacity: .7; 13 | } 14 | 15 | .glyphicon { 16 | position: absolute; 17 | top: 50%; 18 | left: 50%; 19 | transform: translate(-50%, -50%); 20 | font-size: 50px; 21 | opacity: .5; 22 | color: white; 23 | } 24 | 25 | img { 26 | height: 200px; 27 | width: 200px; 28 | display: inline-block; 29 | } 30 | 31 | video { 32 | height: 200px; 33 | width: 200px; 34 | vertical-align: middle; 35 | background-color: black; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /imgur-client/sass/style.scss: -------------------------------------------------------------------------------- 1 | .black { 2 | color: black 3 | } 4 | -------------------------------------------------------------------------------- /imgur-client/sass/topic.scss: -------------------------------------------------------------------------------- 1 | .topic { 2 | text-align: center; 3 | 4 | .image-preview { 5 | animation: fadein .65s ease-out; 6 | 7 | @keyframes fadein { 8 | 0% { 9 | opacity: 0; 10 | } 11 | 100% { 12 | opacity: 1.0; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /imgur-client/src/actions.jsx: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | 3 | module.exports = Reflux.createActions([ 4 | 'getTopics', 5 | 'getImages', 6 | 'getImage' 7 | ]); 8 | -------------------------------------------------------------------------------- /imgur-client/src/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Routes = require('./routes'); 3 | var Api = require('./utils/api'); 4 | 5 | React.render(Routes, document.querySelector('.container')); 6 | -------------------------------------------------------------------------------- /imgur-client/src/components/comment-box.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | render: function(){ 5 | return 8 | }, 9 | renderComments: function() { 10 | return this.props.comments.slice(0, 20).map(function(comment){ 11 | return
  • 12 | {comment.ups} 13 |
    {comment.author}
    14 | {comment.comment} 15 |
  • 16 | }) 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /imgur-client/src/components/header.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Router = require('react-router'); 3 | var Link = Router.Link; 4 | var Actions = require('../actions'); 5 | var TopicStore = require('../stores/topic-store'); 6 | var Reflux = require('reflux'); 7 | 8 | module.exports = React.createClass({ 9 | mixins: [ 10 | Reflux.listenTo(TopicStore, 'onChange') 11 | ], 12 | getInitialState: function() { 13 | return { 14 | topics: [] 15 | } 16 | }, 17 | componentWillMount: function() { 18 | Actions.getTopics(); 19 | }, 20 | render: function() { 21 | return 31 | }, 32 | renderTopics: function() { 33 | return this.state.topics.slice(0, 4).map(function(topic){ 34 | return
  • 35 | 36 | {topic.name} 37 | 38 |
  • 39 | }); 40 | }, 41 | onChange: function(event, topics) { 42 | this.setState({ 43 | topics: topics 44 | }); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /imgur-client/src/components/image-detail.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Reflux = require('reflux'); 3 | var ImageStore = require('../stores/image-store'); 4 | var CommentStore = require('../stores/comment-store'); 5 | var Actions = require('../actions'); 6 | var CommentBox = require('./comment-box'); 7 | 8 | module.exports = React.createClass({ 9 | mixins: [ 10 | Reflux.listenTo(ImageStore, 'onChange'), 11 | Reflux.listenTo(CommentStore, 'onChange') 12 | ], 13 | getInitialState: function() { 14 | return { 15 | image: null, 16 | comment: null 17 | } 18 | }, 19 | componentWillMount: function() { 20 | Actions.getImage(this.props.params.id); 21 | }, 22 | render: function() { 23 | return
    24 | {this.state.image ? this.renderContent() : null} 25 |
    26 | }, 27 | renderContent: function() { 28 | return
    29 |
    30 |
    31 |

    {this.state.image.title}

    32 |
    33 |
    34 | {this.renderImage()} 35 |
    36 |
    37 |
    {this.state.image.description}
    38 |
    39 |
    40 |

    Comments

    41 | {this.renderComments()} 42 |
    43 | }, 44 | renderComments: function() { 45 | if(!this.state.comments){ 46 | return null 47 | } 48 | 49 | return 50 | }, 51 | renderImage: function() { 52 | if(this.state.image.animated) { 53 | return 56 | } else { 57 | return 58 | } 59 | }, 60 | onChange: function() { 61 | this.setState({ 62 | image: ImageStore.find(this.props.params.id), 63 | comments: CommentStore.comment 64 | }); 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /imgur-client/src/components/image-preview.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactRouter = require('react-router'); 3 | var Link = ReactRouter.Link; 4 | 5 | module.exports = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | hovering: false 9 | } 10 | }, 11 | render: function() { 12 | return 18 | {this.props.animated && this.state.hovering ? this.video() : this.image()} 19 | {this.props.animated && !this.state.hovering ? this.icon() : null } 20 | {this.state.hovering ? this.inset() : null} 21 | 22 | }, 23 | inset: function() { 24 | return
    25 | Views: {this.props.views} 26 |
    27 | Upvotes: {this.props.ups} 28 |
    29 | }, 30 | image: function() { 31 | var link = 'http://i.imgur.com/' + this.props.id + 'h.jpg'; 32 | 33 | return 34 | }, 35 | video: function() { 36 | return
    37 | 40 |
    41 | }, 42 | icon: function() { 43 | return 44 | }, 45 | handleMouseEnter: function() { 46 | this.setState({hovering: true}); 47 | }, 48 | handleMouseLeave: function() { 49 | this.setState({hovering: false}); 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /imgur-client/src/components/main.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Header = require('./header'); 3 | var TopicList = require('./topic-list'); 4 | 5 | module.exports = React.createClass({ 6 | render: function() { 7 | return
    8 |
    9 | {this.content()} 10 |
    11 | }, 12 | content: function() { 13 | if(this.props.children) { 14 | return this.props.children 15 | } else { 16 | return 17 | } 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /imgur-client/src/components/topic-list.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Reflux = require('reflux'); 3 | var TopicStore = require('../stores/topic-store'); 4 | var Actions = require('../actions'); 5 | var ReactRouter = require('react-router'); 6 | var Link = ReactRouter.Link; 7 | 8 | module.exports = React.createClass({ 9 | mixins: [ 10 | Reflux.listenTo(TopicStore, 'onChange') 11 | ], 12 | getInitialState: function() { 13 | return { 14 | topics: [] 15 | } 16 | }, 17 | componentWillMount: function() { 18 | Actions.getTopics(); 19 | }, 20 | render: function() { 21 | return
    22 | {this.renderTopics()} 23 |
    24 | }, 25 | renderTopics: function() { 26 | return this.state.topics.slice(0, 4).map(function(topic){ 27 | return 28 |

    {topic.name}

    29 |

    {topic.description}

    30 | 31 | }); 32 | }, 33 | onChange: function(event, topics) { 34 | this.setState({topics: topics}); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /imgur-client/src/components/topic.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Actions = require('../actions'); 3 | var ImageStore = require('../stores/image-store'); 4 | var Reflux = require('reflux'); 5 | var ImagePreview = require('./image-preview'); 6 | 7 | module.exports = React.createClass({ 8 | mixins: [ 9 | Reflux.listenTo(ImageStore, 'onChange') 10 | ], 11 | getInitialState: function() { 12 | return { 13 | images: [] 14 | } 15 | }, 16 | componentWillMount: function() { 17 | Actions.getImages(this.props.params.id); 18 | }, 19 | componentWillReceiveProps: function(nextProps){ 20 | Actions.getImages(nextProps.params.id); 21 | }, 22 | render: function() { 23 | return
    24 | {this.renderImages()} 25 |
    26 | }, 27 | renderImages: function() { 28 | return this.state.images.slice(0, 20).map(function(image) { 29 | return 30 | }); 31 | }, 32 | onChange: function(event, images) { 33 | this.setState({images: images}) 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /imgur-client/src/routes.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactRouter = require('react-router'); 3 | var HashHistory = require('react-router/lib/hashhistory'); 4 | var Router = ReactRouter.Router; 5 | var Route = ReactRouter.Route; 6 | 7 | var Main = require('./components/main'); 8 | var Topic = require('./components/topic'); 9 | var ImageDetail = require('./components/image-detail'); 10 | 11 | module.exports = ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | -------------------------------------------------------------------------------- /imgur-client/src/stores/comment-store.jsx: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | var Actions = require('../actions'); 3 | var Api = require('../utils/api'); 4 | 5 | module.exports = Reflux.createStore({ 6 | listenables: [Actions], 7 | getImage: function(id){ 8 | Api.get('gallery/' + id + '/comments') 9 | .then(function(json){ 10 | this.comment = json.data; 11 | this.triggerChange(); 12 | }.bind(this)); 13 | }, 14 | triggerChange: function() { 15 | this.trigger('change', this.comment); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /imgur-client/src/stores/image-store.jsx: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | var Api = require('../utils/api'); 3 | var Actions = require('../actions'); 4 | var _ = require('lodash'); 5 | 6 | module.exports = Reflux.createStore({ 7 | listenables: [Actions], 8 | getImages: function(topicId){ 9 | Api.get('topics/' + topicId) 10 | .then(function(json){ 11 | this.images = _.reject(json.data, function(image) { 12 | return image.is_album 13 | }); 14 | 15 | this.triggerChange(); 16 | }.bind(this)); 17 | }, 18 | getImage: function(id) { 19 | Api.get('gallery/image/' + id) 20 | .then(function(json){ 21 | if(this.images){ 22 | this.images.push(json.data); 23 | } else { 24 | this.images = [json.data]; 25 | } 26 | 27 | this.triggerChange(); 28 | }.bind(this)); 29 | }, 30 | find: function(id){ 31 | var image = _.findWhere(this.images, {id: id}); 32 | 33 | if(image) { 34 | return image 35 | } else { 36 | this.getImage(id); 37 | return null 38 | } 39 | }, 40 | triggerChange: function() { 41 | this.trigger('change', this.images); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /imgur-client/src/stores/topic-store.jsx: -------------------------------------------------------------------------------- 1 | var Api = require('../utils/api'); 2 | var Reflux = require('reflux'); 3 | var Actions = require('../actions'); 4 | 5 | module.exports = Reflux.createStore({ 6 | listenables: [Actions], 7 | getTopics: function() { 8 | return Api.get('topics/defaults') 9 | .then(function(json){ 10 | this.topics = json.data; 11 | this.triggerChange(); 12 | }.bind(this)); 13 | }, 14 | triggerChange: function() { 15 | this.trigger('change', this.topics); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /imgur-client/src/utils/api.jsx: -------------------------------------------------------------------------------- 1 | var Fetch = require('whatwg-fetch'); 2 | var rootUrl = 'https://api.imgur.com/3/'; 3 | var apiKey = '430d6820d865788'; 4 | 5 | module.exports = { 6 | get: function(url) { 7 | return fetch(rootUrl + url, { 8 | headers: { 9 | 'Authorization': 'Client-ID ' + apiKey 10 | } 11 | }) 12 | .then(function(response){ 13 | return response.json() 14 | }) 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /thumbnail-gulp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | main.js 3 | -------------------------------------------------------------------------------- /thumbnail-gulp/application.js: -------------------------------------------------------------------------------- 1 | var options = { 2 | thumbnailData: [{ 3 | title: 'Show Courses', 4 | number: 12, 5 | header: 'Learn React', 6 | description: 'React is a fantastic new front end library for rendering web pages. React is a fantastic new front end library for rendering web pages.', 7 | imageUrl: 'https://raw.githubusercontent.com/wiki/facebook/react/react-logo-1000-transparent.png' 8 | },{ 9 | title: 'Show Courses', 10 | number: 25, 11 | header: 'Learn Gulp', 12 | description: 'Gulp will speed up your development workflow. Gulp will speed up your development workflow. Gulp will speed up your development workflow.', 13 | imageUrl: 'http://brunch.io/images/others/gulp.png' 14 | }] 15 | }; 16 | 17 | var element = React.createElement(ThumbnailList, options); 18 | React.render(element, document.querySelector('.container')); 19 | 20 | var Badge = React.createClass({displayName: "Badge", 21 | render: function() { 22 | return React.createElement("button", {className: "btn btn-primary", type: "button"}, 23 | this.props.title, " ", React.createElement("span", {className: "badge"}, this.props.number) 24 | ) 25 | } 26 | }); 27 | 28 | var ThumbnailList = React.createClass({displayName: "ThumbnailList", 29 | render: function() { 30 | var list = this.props.thumbnailData.map(function(thumbnailProps){ 31 | return React.createElement(Thumbnail, React.__spread({}, thumbnailProps)) 32 | }); 33 | 34 | return React.createElement("div", null, 35 | list 36 | ) 37 | } 38 | }); 39 | 40 | var Thumbnail = React.createClass({displayName: "Thumbnail", 41 | render: function() { 42 | return React.createElement("div", {className: "col-sm-6 col-md-4"}, 43 | React.createElement("div", {className: "thumbnail"}, 44 | React.createElement("img", {src: this.props.imageUrl, alt: "..."}), 45 | React.createElement("div", {className: "caption"}, 46 | React.createElement("h3", null, this.props.header), 47 | React.createElement("p", null, this.props.description), 48 | React.createElement("p", null, 49 | React.createElement(Badge, {title: this.props.title, number: this.props.number}) 50 | ) 51 | ) 52 | ) 53 | ) 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /thumbnail-gulp/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gutil = require('gulp-util'); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | var watchify = require('watchify'); 6 | var reactify = require('reactify'); 7 | 8 | gulp.task('default', function() { 9 | var bundler = watchify(browserify({ 10 | entries: ['./src/app.jsx'], 11 | transform: [reactify], 12 | extensions: ['.jsx'], 13 | debug: true, 14 | cache: {}, 15 | packageCache: {}, 16 | fullPaths: true 17 | })); 18 | 19 | function build(file) { 20 | if (file) gutil.log('Recompiling ' + file); 21 | return bundler 22 | .bundle() 23 | .on('error', gutil.log.bind(gutil, 'Browserify Error')) 24 | .pipe(source('main.js')) 25 | .pipe(gulp.dest('./')); 26 | }; 27 | build(); 28 | bundler.on('update', build); 29 | }); 30 | -------------------------------------------------------------------------------- /thumbnail-gulp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /thumbnail-gulp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thumbnail-gulp", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "browserify": "^9.0.3", 12 | "gulp": "^3.8.11", 13 | "gulp-concat": "^2.5.2", 14 | "gulp-react": "^3.0.1", 15 | "gulp-util": "^3.0.4", 16 | "react": "^0.13.3", 17 | "reactify": "^1.1.0", 18 | "vinyl-source-stream": "^1.1.0", 19 | "watchify": "^2.4.0" 20 | }, 21 | "devDependencies": {} 22 | } 23 | -------------------------------------------------------------------------------- /thumbnail-gulp/src/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ThumbnailList = require('./thumbnail-list'); 3 | 4 | var options = { 5 | thumbnailData: [{ 6 | title: 'Show Courses', 7 | number: 120, 8 | header: 'Learn React', 9 | description: 'React is a fantastic new front end library for rendering web pages. React is a fantastic new front end library for rendering web pages.', 10 | imageUrl: 'https://raw.githubusercontent.com/wiki/facebook/react/react-logo-1000-transparent.png' 11 | },{ 12 | title: 'Show Courses', 13 | number: 25, 14 | header: 'Learn Gulp', 15 | description: 'Gulp will speed up your development workflow. Gulp will speed up your development workflow. Gulp will speed up your development workflow.', 16 | imageUrl: 'http://brunch.io/images/others/gulp.png' 17 | }] 18 | }; 19 | 20 | 21 | var element = React.createElement(ThumbnailList, options); 22 | React.render(element, document.querySelector('.container')); 23 | -------------------------------------------------------------------------------- /thumbnail-gulp/src/badge.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | render: function() { 5 | return 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /thumbnail-gulp/src/thumbnail-list.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Thumbnail = require('./thumbnail'); 3 | 4 | module.exports = React.createClass({ 5 | render: function() { 6 | var list = this.props.thumbnailData.map(function(thumbnailProps){ 7 | return 8 | }); 9 | 10 | return
    11 | {list} 12 |
    13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /thumbnail-gulp/src/thumbnail.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Badge = require('./badge'); 3 | 4 | module.exports = React.createClass({ 5 | render: function() { 6 | return
    7 |
    8 | ... 9 |
    10 |

    {this.props.header}

    11 |

    {this.props.description}

    12 |

    13 | 14 |

    15 |
    16 |
    17 |
    18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /thumbnail-list/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    8 |
    9 | 10 | 11 | 68 | -------------------------------------------------------------------------------- /thumbnail/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    8 |
    9 | 10 | 11 | 48 | -------------------------------------------------------------------------------- /todos/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | main.js 3 | -------------------------------------------------------------------------------- /todos/README.md: -------------------------------------------------------------------------------- 1 | ReactStarter 2 | ==== 3 | 4 | Use this as a starting point for working on chapters of the [Learn and Understand React JS](https://www.udemy.com/learn-and-understand-reactjs/) course on Udemy.com. 5 | 6 | --- 7 | 8 | ###Getting Started### 9 | 10 | There are two methods for getting started with this repo. 11 | 12 | ####Familiar with Git?##### 13 | Checkout this repo, install depdencies, then start the gulp process with the following: 14 | 15 | ``` 16 | > git clone git@github.com:StephenGrider/ReactStarter.git 17 | > cd ReactStarter 18 | > npm install 19 | > gulp 20 | ``` 21 | 22 | ####Not Familiar with Git?##### 23 | Click [here](https://github.com/StephenGrider/ReactStarter/releases) then download the .zip file. Extract the contents of the zip file, then open your terminal, change to the project directory, and: 24 | 25 | ``` 26 | > npm install 27 | > gulp 28 | ``` 29 | -------------------------------------------------------------------------------- /todos/application.js: -------------------------------------------------------------------------------- 1 | var options = { 2 | thumbnailData: [{ 3 | title: 'Show Courses', 4 | number: 12, 5 | header: 'Learn React', 6 | description: 'React is a fantastic new front end library for rendering web pages. React is a fantastic new front end library for rendering web pages.', 7 | imageUrl: 'https://raw.githubusercontent.com/wiki/facebook/react/react-logo-1000-transparent.png' 8 | },{ 9 | title: 'Show Courses', 10 | number: 25, 11 | header: 'Learn Gulp', 12 | description: 'Gulp will speed up your development workflow. Gulp will speed up your development workflow. Gulp will speed up your development workflow.', 13 | imageUrl: 'http://brunch.io/images/others/gulp.png' 14 | }] 15 | }; 16 | 17 | var element = React.createElement(ThumbnailList, options); 18 | React.render(element, document.querySelector('.container')); 19 | 20 | var Badge = React.createClass({displayName: "Badge", 21 | render: function() { 22 | return React.createElement("button", {className: "btn btn-primary", type: "button"}, 23 | this.props.title, " ", React.createElement("span", {className: "badge"}, this.props.number) 24 | ) 25 | } 26 | }); 27 | 28 | var ThumbnailList = React.createClass({displayName: "ThumbnailList", 29 | render: function() { 30 | var list = this.props.thumbnailData.map(function(thumbnailProps){ 31 | return React.createElement(Thumbnail, React.__spread({}, thumbnailProps)) 32 | }); 33 | 34 | return React.createElement("div", null, 35 | list 36 | ) 37 | } 38 | }); 39 | 40 | var Thumbnail = React.createClass({displayName: "Thumbnail", 41 | render: function() { 42 | return React.createElement("div", {className: "col-sm-6 col-md-4"}, 43 | React.createElement("div", {className: "thumbnail"}, 44 | React.createElement("img", {src: this.props.imageUrl, alt: "..."}), 45 | React.createElement("div", {className: "caption"}, 46 | React.createElement("h3", null, this.props.header), 47 | React.createElement("p", null, this.props.description), 48 | React.createElement("p", null, 49 | React.createElement(Badge, {title: this.props.title, number: this.props.number}) 50 | ) 51 | ) 52 | ) 53 | ) 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /todos/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gutil = require('gulp-util'); 3 | var source = require('vinyl-source-stream'); 4 | var browserify = require('browserify'); 5 | var watchify = require('watchify'); 6 | var reactify = require('reactify'); 7 | var notifier = require('node-notifier'); 8 | var server = require('gulp-server-livereload'); 9 | 10 | var notify = function(error) { 11 | var message = 'In: '; 12 | var title = 'Error: '; 13 | 14 | if(error.description) { 15 | title += error.description; 16 | } else if (error.message) { 17 | title += error.message; 18 | } 19 | 20 | if(error.filename) { 21 | var file = error.filename.split('/'); 22 | message += file[file.length-1]; 23 | } 24 | 25 | if(error.lineNumber) { 26 | message += '\nOn Line: ' + error.lineNumber; 27 | } 28 | 29 | notifier.notify({title: title, message: message}); 30 | }; 31 | 32 | var bundler = watchify(browserify({ 33 | entries: ['./src/app.jsx'], 34 | transform: [reactify], 35 | extensions: ['.jsx'], 36 | debug: true, 37 | cache: {}, 38 | packageCache: {}, 39 | fullPaths: true 40 | })); 41 | 42 | function bundle() { 43 | return bundler 44 | .bundle() 45 | .on('error', notify) 46 | .pipe(source('main.js')) 47 | .pipe(gulp.dest('./')) 48 | } 49 | bundler.on('update', bundle) 50 | 51 | gulp.task('build', function() { 52 | bundle() 53 | }); 54 | 55 | gulp.task('serve', function(done) { 56 | gulp.src('') 57 | .pipe(server({ 58 | livereload: { 59 | enable: true, 60 | filter: function(filePath, cb) { 61 | cb( /main.js/.test(filePath) ) 62 | } 63 | }, 64 | open: true 65 | })); 66 | }); 67 | 68 | gulp.task('default', ['build', 'serve']); 69 | -------------------------------------------------------------------------------- /todos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 |
    21 |
    22 | 23 | 24 | -------------------------------------------------------------------------------- /todos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-starter", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "browserify": "^9.0.3", 12 | "firebase": "^2.2.6", 13 | "gulp": "^3.8.11", 14 | "gulp-concat": "^2.5.2", 15 | "gulp-react": "^3.0.1", 16 | "gulp-server-livereload": "^1.3.0", 17 | "gulp-util": "^3.0.4", 18 | "node-notifier": "^4.2.1", 19 | "react": "^0.13.1", 20 | "reactfire": "^0.4.0", 21 | "reactify": "^1.1.0", 22 | "vinyl-source-stream": "^1.1.0", 23 | "watchify": "^2.4.0" 24 | }, 25 | "devDependencies": {} 26 | } 27 | -------------------------------------------------------------------------------- /todos/src/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactFire = require('reactfire'); 3 | var Firebase = require('firebase'); 4 | var Header = require('./header'); 5 | var List = require('./list'); 6 | var rootUrl = 'https://blistering-torch-4253.firebaseio.com/'; 7 | 8 | var App = React.createClass({ 9 | mixins: [ ReactFire ], 10 | getInitialState: function() { 11 | return { 12 | items: {}, 13 | loaded: false 14 | } 15 | }, 16 | componentWillMount: function() { 17 | this.fb = new Firebase(rootUrl + 'items/'); 18 | this.bindAsObject(this.fb, 'items'); 19 | this.fb.on('value', this.handleDataLoaded); 20 | }, 21 | render: function() { 22 | return
    23 |
    24 |

    25 | To-Do List 26 |

    27 |
    28 |
    29 |
    30 | 31 | {this.deleteButton()} 32 |
    33 |
    34 |
    35 | }, 36 | deleteButton: function() { 37 | if(!this.state.loaded) { 38 | return 39 | } else { 40 | return
    41 |
    42 | 48 |
    49 | } 50 | }, 51 | onDeleteDoneClick: function() { 52 | for(var key in this.state.items) { 53 | if(this.state.items[key].done === true) { 54 | this.fb.child(key).remove(); 55 | } 56 | } 57 | }, 58 | handleDataLoaded: function(){ 59 | this.setState({loaded: true}); 60 | } 61 | }); 62 | 63 | var element = React.createElement(App, {}); 64 | React.render(element, document.querySelector('.container')); 65 | -------------------------------------------------------------------------------- /todos/src/header.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | getInitialState: function() { 5 | return { 6 | text: '' 7 | } 8 | }, 9 | render: function() { 10 | return
    11 | 16 | 17 | 23 | 24 |
    25 | }, 26 | handleClick: function() { 27 | this.props.itemsStore.push({ 28 | text: this.state.text, 29 | done: false 30 | }); 31 | 32 | this.setState({text: ''}); 33 | }, 34 | handleInputChange: function(event) { 35 | this.setState({text: event.target.value}); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /todos/src/list-item.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Firebase = require('firebase'); 3 | var rootUrl = 'https://blistering-torch-4253.firebaseio.com/'; 4 | 5 | module.exports = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | text: this.props.item.text, 9 | done: this.props.item.done, 10 | textChanged: false 11 | } 12 | }, 13 | componentWillMount: function() { 14 | this.fb = new Firebase(rootUrl + 'items/' + this.props.item.key); 15 | }, 16 | render: function() { 17 | return
    18 | 19 | 24 | 25 | 31 | 32 | {this.changesButtons()} 33 | 39 | 40 |
    41 | }, 42 | changesButtons: function() { 43 | if(!this.state.textChanged) { 44 | return null 45 | } else { 46 | return [ 47 | , 53 | 59 | ] 60 | } 61 | }, 62 | handleSaveClick: function() { 63 | this.fb.update({text: this.state.text}); 64 | this.setState({textChanged: false}); 65 | }, 66 | handleUndoClick: function() { 67 | this.setState({ 68 | text: this.props.item.text, 69 | textChanged: false 70 | }); 71 | }, 72 | handleTextChange: function(event) { 73 | this.setState({ 74 | text: event.target.value, 75 | textChanged: true 76 | }); 77 | }, 78 | handleDoneChange: function(event) { 79 | var update = {done: event.target.checked} 80 | this.setState(update); 81 | this.fb.update(update); 82 | }, 83 | handleDeleteClick: function() { 84 | this.fb.remove(); 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /todos/src/list.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ListItem = require('./list-item'); 3 | 4 | module.exports = React.createClass({ 5 | render: function() { 6 | return
    7 | {this.renderList()} 8 |
    9 | }, 10 | renderList: function() { 11 | if(!this.props.items) { 12 | return

    13 | Add a todo to get started. 14 |

    15 | } else { 16 | var children = []; 17 | 18 | for(var key in this.props.items) { 19 | var item = this.props.items[key]; 20 | item.key = key; 21 | 22 | children.push( 23 | 27 | 28 | ) 29 | } 30 | 31 | return children; 32 | } 33 | } 34 | }); 35 | --------------------------------------------------------------------------------