diff --git a/.gitignore b/.gitignore index f8d1970..b7cd026 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ npm-debug.log **/node_modules/* build/* +.idea diff --git a/ep19-call-api-jquery/README.md b/ep19-call-api-jquery/README.md new file mode 100644 index 0000000..0f6225e --- /dev/null +++ b/ep19-call-api-jquery/README.md @@ -0,0 +1,13 @@ +#### Setting up the application + +``` +npm install +npm start +``` + +Visit http://localhost:8080 in browser. + +#### Notes + +* `npm install random-key --save` +* [Source code](...) diff --git a/ep19-call-api-jquery/app/components/App.jsx b/ep19-call-api-jquery/app/components/App.jsx new file mode 100644 index 0000000..9bae099 --- /dev/null +++ b/ep19-call-api-jquery/app/components/App.jsx @@ -0,0 +1,97 @@ +import React from 'react'; +import DisplayList from './DisplayList'; + +var rand = require('random-key'); +var api = require("../utils/api"); + +export default class App extends React.Component { + + constructor () { + super(); + this.state = { title: '', todos: [] }; + var processData = function(data) { + this.setState({todos: data.todos}); + }; + api.getTasks(processData.bind(this)); + } + + handleDone (idToBeMarkedAsDone) { + var _todos = this.state.todos; + var todo = _todos.filter((todo) => { + return todo.id === idToBeMarkedAsDone; + })[0]; + + todo.done = !todo.done; + + var processData = function(data) { + this.setState({todos: data.todos}); + }; + + var markTaskDoneCallback = function(data){ + data.success ? api.getTasks(processData.bind(this)) : console.log("Failed to mark task as done/undone"); + }; + + api.markTaskDone(markTaskDoneCallback.bind(this), todo); + } + + handleDelete (idToBeDeleted) { + var processData = function(data) { + this.setState({todos: data.todos}); + }; + + var deleteTaskCallback = function(data){ + data.success ? api.getTasks(processData.bind(this)) : console.log("Failed to delete task"); + }; + + api.deleteTask(deleteTaskCallback.bind(this), idToBeDeleted); + } + + handleSubmit (event) { + event.preventDefault(); + + var newTodo = { title: this.state.title, done: false }; + + var processData = function(data) { + this.setState({title: '', todos: data.todos}); + }; + + var addTaskCallback = function(data){ + data.success ? api.getTasks(processData.bind(this)) : console.log("Failed to add task"); + }; + + api.addTask(addTaskCallback.bind(this), newTodo); + } + + handleChange (event) { + var title = event.target.value; + this.setState({ title: title }); + } + + handleClearCompleted (event) { + var newTodos = this.state.todos.filter((todo) => { return !todo.done}); + this.setState({ todos: newTodos }); + } + + render () { + return
+

TODO

+
+ +
+ + + + +
; + } +} diff --git a/ep19-call-api-jquery/app/components/DisplayItem.jsx b/ep19-call-api-jquery/app/components/DisplayItem.jsx new file mode 100644 index 0000000..e91c72b --- /dev/null +++ b/ep19-call-api-jquery/app/components/DisplayItem.jsx @@ -0,0 +1,74 @@ +import React from 'react'; + +export default class DisplayItem extends React.Component { + + constructor () { + super(); + this.state = { editing: false } + } + + componentDidMount () { + this.setState({ changedText: this.props.todo.title }); + } + + handleEditing (event) { + this.setState({ editing: true, changedText: this.props.todo.title }); + } + + handleEditingDone (event) { + if (event.keyCode === 13 ) { // submit + this.setState({ editing: false }); + } + } + + handleEditingChange (event) { + var _changedText = event.target.value; + this.setState({ changedText: _changedText }); + } + + render () { + var todo = this.props.todo; + + var viewStyle = {}; + var editStyle = {}; + + if (this.state.editing) { + viewStyle.display = 'none'; + } else { + editStyle.display = 'none'; + } + + return
  • +
    + + + + + + [x] + +
    + + +
  • + } + +} + +DisplayItem.propTypes = { + todo: React.PropTypes.object.isRequired, + handleDone: React.PropTypes.func.isRequired, + handleDelete: React.PropTypes.func.isRequired +} diff --git a/ep19-call-api-jquery/app/components/DisplayList.jsx b/ep19-call-api-jquery/app/components/DisplayList.jsx new file mode 100644 index 0000000..0b4daec --- /dev/null +++ b/ep19-call-api-jquery/app/components/DisplayList.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import DisplayItem from './DisplayItem'; + +export default class DisplayList extends React.Component { + + render () { + return + } + +} + +DisplayList.propTypes = { + todos: React.PropTypes.array.isRequired, + handleDone: React.PropTypes.func.isRequired, + handleDelete: React.PropTypes.func.isRequired +} diff --git a/ep19-call-api-jquery/app/main.jsx b/ep19-call-api-jquery/app/main.jsx new file mode 100644 index 0000000..aee7cdf --- /dev/null +++ b/ep19-call-api-jquery/app/main.jsx @@ -0,0 +1,14 @@ +import './stylesheets/main.css'; + +import React from 'react'; +import App from './components/App'; + +main(); + +function main() { + var div = document.createElement('div'); + div.setAttribute("id", "todoapp"); + document.body.appendChild(div); + + React.render(, div); +} diff --git a/ep19-call-api-jquery/app/stylesheets/main.css b/ep19-call-api-jquery/app/stylesheets/main.css new file mode 100644 index 0000000..ac2d81f --- /dev/null +++ b/ep19-call-api-jquery/app/stylesheets/main.css @@ -0,0 +1,190 @@ +html, +body { + margin: 0; + padding: 0; +} + +body { + font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eeeeee; + color: #333333; + width: 520px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; +} + +#todoapp { + background: #fff; + padding: 20px; + margin-bottom: 40px; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; + -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; + box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; + -webkit-border-radius: 0 0 5px 5px; + -moz-border-radius: 0 0 5px 5px; + -ms-border-radius: 0 0 5px 5px; + -o-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; +} + +#todoapp h1 { + font-size: 36px; + font-weight: bold; + text-align: center; + padding: 0 0 10px 0; +} + +#todoapp input[type="text"] { + width: 466px; + font-size: 24px; + font-family: inherit; + line-height: 1.4em; + border: 0; + outline: none; + padding: 6px; + border: 1px solid #999999; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; + -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; +} + +#todo-list { + margin: 10px 0; + padding: 0; + list-style: none; +} + +#todo-list li { + padding: 18px 20px 18px 0; + position: relative; + font-size: 24px; + border-bottom: 1px solid #cccccc; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.done label { + color: #777777; + text-decoration: line-through; +} + +#todo-list .destroy { + position: absolute; + right: 5px; + top: 20px; + display: none; + cursor: pointer; + width: 20px; + height: 20px; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list .destroy:hover { + background-position: 0 -20px; +} + +#todo-list li.editing { + border-bottom: none; + margin-top: -1px; + padding: 0; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#todo-list li .view label { + word-break: break-word; +} + +#todoapp footer { + margin: 0 -20px -20px -20px; + overflow: hidden; + color: #555555; + background: #f4fce8; + border-top: 1px solid #ededed; + padding: 0 20px; + line-height: 37px; + -webkit-border-radius: 0 0 5px 5px; + -moz-border-radius: 0 0 5px 5px; + -ms-border-radius: 0 0 5px 5px; + -o-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; +} + +#clear-completed { + float: right; + line-height: 20px; + text-decoration: none; + background: rgba(0, 0, 0, 0.1); + color: #555555; + font-size: 11px; + margin-top: 8px; + margin-bottom: 8px; + padding: 0 10px 1px; + cursor: pointer; + -webkit-border-radius: 12px; + -moz-border-radius: 12px; + -ms-border-radius: 12px; + -o-border-radius: 12px; + border-radius: 12px; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; + -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; + box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; +} + +#clear-completed:hover { + background: rgba(0, 0, 0, 0.15); + -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; + -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; + -ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; + -o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; + box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; +} + +#clear-completed:active { + position: relative; + top: 1px; +} + +#todo-count span { + font-weight: bold; +} + +#instructions { + margin: 10px auto; + color: #777777; + text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; + text-align: center; +} + +#instructions a { + color: #336699; +} + +#credits { + margin: 30px auto; + color: #999; + text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; + text-align: center; +} + +#credits a { + color: #888; +} diff --git a/ep19-call-api-jquery/app/utils/api.js b/ep19-call-api-jquery/app/utils/api.js new file mode 100644 index 0000000..8e5a67d --- /dev/null +++ b/ep19-call-api-jquery/app/utils/api.js @@ -0,0 +1,46 @@ +var Constants = require("./constants"); +var $ = require('jquery'); + +var api = { + getTasks (processData) { + var url = Constants.BASE_URL + 'todos'; + this.makeAjaxCall(url, 'GET', processData) + }, + + markTaskDone (processData, todo) { + var url = Constants.BASE_URL + 'todos/' + todo.id; + var params = { done: todo.done }; + this.makeAjaxCall(url, 'PUT', processData, params) + }, + + deleteTask (processData, idToBeDeleted) { + var url = Constants.BASE_URL + 'todos/' + idToBeDeleted; + this.makeAjaxCall(url, 'DELETE', processData) + }, + + addTask (processData, todo) { + var url = Constants.BASE_URL + 'todos'; + this.makeAjaxCall(url, 'POST', processData, todo) + }, + + makeAjaxCall (url, type, processDataCallback, params) { + $.ajax({ + type: type, + url: url, + data: { + api_key: Constants.API_KEY, + todo: params + }, + dataType: 'json', + success: function(data) { + console.log(data); + processDataCallback(data); + }, + error: function() { + console.log("An error has occurred"); + } + }); + } +}; + +module.exports = api; diff --git a/ep19-call-api-jquery/app/utils/constants.js b/ep19-call-api-jquery/app/utils/constants.js new file mode 100644 index 0000000..9c0170d --- /dev/null +++ b/ep19-call-api-jquery/app/utils/constants.js @@ -0,0 +1,6 @@ +var Constants = { + BASE_URL: "http://lrjis-api-production.herokuapp.com/api/v1/", + API_KEY: "add your api key here" +}; + +module.exports = Constants; diff --git a/ep19-call-api-jquery/package.json b/ep19-call-api-jquery/package.json new file mode 100644 index 0000000..cc1023e --- /dev/null +++ b/ep19-call-api-jquery/package.json @@ -0,0 +1,30 @@ +{ + "name": "learning-reactjs-in-steps", + "version": "0.0.1", + "description": "Learn ReactJS in steps", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "TAGET=build webpack", + "start": "TARGET=dev webpack-dev-server --devtool eval --progress --colors --hot" + }, + "author": "Neeraj Singh", + "license": "ISC", + "devDependencies": { + "babel-core": "^5.6.15", + "babel-loader": "^5.3.1", + "css-loader": "^0.15.1", + "html-webpack-plugin": "^1.5.2", + "node-libs-browser": "^0.5.2", + "react-hot-loader": "^1.2.7", + "style-loader": "^0.12.3", + "webpack": "^1.10.1", + "webpack-dev-server": "^1.10.1", + "webpack-merge": "^0.1.2" + }, + "dependencies": { + "jquery": "1.11.3", + "random-key": "^0.3.2", + "react": "^0.13.3" + } +} diff --git a/ep19-call-api-jquery/webpack.config.js b/ep19-call-api-jquery/webpack.config.js new file mode 100644 index 0000000..bb8e6c1 --- /dev/null +++ b/ep19-call-api-jquery/webpack.config.js @@ -0,0 +1,54 @@ +var path = require('path'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var merge = require('webpack-merge'); + +var TARGET = process.env.TARGET; +var ROOT_PATH = path.resolve(__dirname); + +var common = { + entry: [path.resolve(ROOT_PATH, 'app/main')], + resolve: { + extensions: ['', '.js', '.jsx'], + }, + output: { + path: path.resolve(ROOT_PATH, 'build'), + filename: 'bundle.js', + }, + plugins: [ + new HtmlWebpackPlugin({ title: 'Todo app', }), + ], + module: { + loaders: [ + { + test: /\.css$/, + loaders: ['style', 'css'], + }, + { + test: /\.jsx?$/, + loader: 'babel?stage=1', + include: path.resolve(ROOT_PATH, 'app'), + } + ], + }, +}; + +if (TARGET === 'build') { + module.exports = common; +} + +if (TARGET === 'dev') { + module.exports = merge(common, { + entry: [ + 'webpack-dev-server/client?http://0.0.0.0:8080', + 'webpack/hot/dev-server' + ], + module: { + loaders: [ { + test: /\.jsx?$/, + loaders: ['react-hot', 'babel?stage=1'], + include: path.resolve(ROOT_PATH, 'app'), + }, + ], + }, + }); +}