From a4c3138cf072a6cb18aa9d8b6e7311e3b3b5b739 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 03:50:59 +0300 Subject: [PATCH 01/16] Init react project (webpack, babel) --- .babelrc | 6 ++++++ .idea/vcs.xml | 6 ++++++ package.json | 37 +++++++++++++++++++++++++++++++++ prod/index.html | 11 ++++++++++ src/components/Header/Header.js | 11 ++++++++++ src/js/app.js | 8 +++++++ webpack.config.js | 29 ++++++++++++++++++++++++++ 7 files changed, 108 insertions(+) create mode 100644 .babelrc create mode 100644 .idea/vcs.xml create mode 100644 package.json create mode 100644 prod/index.html create mode 100644 src/components/Header/Header.js create mode 100644 src/js/app.js create mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..2a4a32d --- /dev/null +++ b/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + "es2015", + "react" + ] +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..fef0e2f --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "react-homework", + "version": "1.0.0", + "description": "Реализовать todomvc из прошлого домашнего задания на чистом React", + "main": "index.js", + "dependencies": { + "react": "^15.4.1", + "react-dom": "^15.4.1", + "todomvc-app-css": "^2.0.6", + "todomvc-common": "^1.0.3", + "webpack": "^1.14.0" + }, + "devDependencies": { + "babel-core": "^6.20.0", + "babel-loader": "^6.2.9", + "babel-preset-es2015": "^6.18.0", + "babel-preset-react": "^6.16.0", + "file-loader": "^0.9.0", + "jsx-loader": "^0.13.2", + "react-hot-loader": "^1.3.1", + "webpack-dev-server": "^1.16.2" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot --inline --history-api-fallback" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/artmaks/react-homework.git" + }, + "author": "artem", + "license": "ISC", + "bugs": { + "url": "https://github.com/artmaks/react-homework/issues" + }, + "homepage": "https://github.com/artmaks/react-homework#readme" +} diff --git a/prod/index.html b/prod/index.html new file mode 100644 index 0000000..62ea3b7 --- /dev/null +++ b/prod/index.html @@ -0,0 +1,11 @@ + + + + Your app name + + + +
+ + + \ No newline at end of file diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js new file mode 100644 index 0000000..6d87566 --- /dev/null +++ b/src/components/Header/Header.js @@ -0,0 +1,11 @@ +import React, { Component } from 'react'; + +export default class Menu extends Component { + render() { + return ( +
+ Header +
+ ); + } +} \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js new file mode 100644 index 0000000..96e11ec --- /dev/null +++ b/src/js/app.js @@ -0,0 +1,8 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Menu from '../components/Header/Menu.jsx.js'; + +ReactDOM.render( +
, + document.getElementById('app') +); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..d0e698e --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,29 @@ +module.exports = { + entry: { + javascript: "./src/js/app.js", + html: "./prod/index.html", + }, + + output: { + filename: "app.js", + path: "./prod", + }, + + resolve: { + extensions: ['', '.js', '.jsx', '.json'] + }, + + module: { + loaders: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + loaders: ["react-hot", "babel-loader"], + }, + { + test: /\.html$/, + loader: "file?name=[name].[ext]", + } + ] + } +}; \ No newline at end of file From a56594453d5b8357a17a8b5a80b64fd346bd8d88 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 04:51:11 +0300 Subject: [PATCH 02/16] Init main components --- package.json | 1 + src/components/Header/Header.js | 6 ++++-- src/components/LargeInput/LargeInput.js | 9 +++++++++ src/components/Todo/Todo.js | 9 +++++++++ src/components/TodoApp/TodoApp.js | 21 +++++++++++++++++++++ src/components/TodoList/TodoList.js | 15 +++++++++++++++ src/js/app.js | 4 ++-- 7 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 src/components/LargeInput/LargeInput.js create mode 100644 src/components/Todo/Todo.js create mode 100644 src/components/TodoApp/TodoApp.js create mode 100644 src/components/TodoList/TodoList.js diff --git a/package.json b/package.json index fef0e2f..97b4dc1 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Реализовать todomvc из прошлого домашнего задания на чистом React", "main": "index.js", "dependencies": { + "express": "^4.14.0", "react": "^15.4.1", "react-dom": "^15.4.1", "todomvc-app-css": "^2.0.6", diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 6d87566..f600607 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,10 +1,12 @@ import React, { Component } from 'react'; +import LargeInput from './../LargeInput/LargeInput' -export default class Menu extends Component { +export default class Header extends Component { render() { return (
- Header +

todos

+
); } diff --git a/src/components/LargeInput/LargeInput.js b/src/components/LargeInput/LargeInput.js new file mode 100644 index 0000000..693d01a --- /dev/null +++ b/src/components/LargeInput/LargeInput.js @@ -0,0 +1,9 @@ +import React, { Component } from 'react'; + +export default class LargeInput extends Component { + render() { + return ( + + ); + } +} \ No newline at end of file diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js new file mode 100644 index 0000000..8ec2726 --- /dev/null +++ b/src/components/Todo/Todo.js @@ -0,0 +1,9 @@ +import React, { Component } from 'react'; + +export default class Todo extends Component { + render() { + return ( +
Todo
+ ); + } +} \ No newline at end of file diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js new file mode 100644 index 0000000..a1054c0 --- /dev/null +++ b/src/components/TodoApp/TodoApp.js @@ -0,0 +1,21 @@ +import React, { Component } from 'react'; +import Header from './../Header/Header' + +export default class Todo extends Component { + + constructor(props) { + super(props); + + this.state = { + items: [], + text: "" + }; + } + render() { + return ( +
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js new file mode 100644 index 0000000..1752d45 --- /dev/null +++ b/src/components/TodoList/TodoList.js @@ -0,0 +1,15 @@ +import React, {Component} from 'react'; + +export default class TodoList extends Component { + render() { + return ( +
+ + {this.props.items.map(item => ( + + ))} + +
+ ); + } +} \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js index 96e11ec..50704f9 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -1,8 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import Menu from '../components/Header/Menu.jsx.js'; +import TodoApp from '../components/TodoApp/TodoApp'; ReactDOM.render( -
, + , document.getElementById('app') ); \ No newline at end of file From a122d2bb34f6f682d4f3afa3ee6e6f1a31ae0815 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 05:19:40 +0300 Subject: [PATCH 03/16] Init onTextChange, onAddTodo events --- src/components/Header/Header.js | 2 +- src/components/LargeInput/LargeInput.js | 9 ++++++++- src/components/TodoApp/TodoApp.js | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index f600607..8f9b898 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -6,7 +6,7 @@ export default class Header extends Component { return (

todos

- +
); } diff --git a/src/components/LargeInput/LargeInput.js b/src/components/LargeInput/LargeInput.js index 693d01a..6c5084b 100644 --- a/src/components/LargeInput/LargeInput.js +++ b/src/components/LargeInput/LargeInput.js @@ -1,9 +1,16 @@ import React, { Component } from 'react'; export default class LargeInput extends Component { + + AddTodoKeyPress(e) { + if (e.keyCode == 13) { + this.props.onAddTodo(e); + } + } + render() { return ( - + ); } } \ No newline at end of file diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index a1054c0..61109ef 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -7,14 +7,29 @@ export default class Todo extends Component { super(props); this.state = { - items: [], - text: "" + todos: [], + inputText: "" }; + + this.onTextChange = this.onTextChange.bind(this); + this.onAddTodo = this.onAddTodo.bind(this); + } + + onTextChange(event) { + this.setState({ + text: event.target.value + }); + console.log(event); } + + onAddTodo(event) { + console.log(event); + } + render() { return (
-
+
); } From 1931885b34307ad903ebc736f5f391a4fffd6c23 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 15:16:11 +0300 Subject: [PATCH 04/16] Add css styles (webpack, style-loader, css-loader) --- package.json | 3 + prod/index.html | 6 +- src/components/Header/Header.css | 12 +++ src/components/Header/Header.js | 4 +- src/components/LargeInput/LargeInput.css | 25 ++++++ src/components/LargeInput/LargeInput.js | 3 +- src/components/Todo/Todo.css | 97 +++++++++++++++++++++ src/components/Todo/Todo.js | 16 +++- src/components/TodoApp/TodoApp.css | 102 +++++++++++++++++++++++ src/components/TodoApp/TodoApp.js | 27 ++++-- src/components/TodoList/TodoList.css | 40 +++++++++ src/components/TodoList/TodoList.js | 16 ++-- src/utils/GUID.js | 16 ++++ webpack.config.js | 12 ++- 14 files changed, 360 insertions(+), 19 deletions(-) create mode 100644 src/components/Header/Header.css create mode 100644 src/components/LargeInput/LargeInput.css create mode 100644 src/components/Todo/Todo.css create mode 100644 src/components/TodoApp/TodoApp.css create mode 100644 src/components/TodoList/TodoList.css create mode 100644 src/utils/GUID.js diff --git a/package.json b/package.json index 97b4dc1..8fa37fa 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,12 @@ "babel-loader": "^6.2.9", "babel-preset-es2015": "^6.18.0", "babel-preset-react": "^6.16.0", + "css-loader": "^0.26.1", + "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "jsx-loader": "^0.13.2", "react-hot-loader": "^1.3.1", + "style-loader": "^0.13.1", "webpack-dev-server": "^1.16.2" }, "scripts": { diff --git a/prod/index.html b/prod/index.html index 62ea3b7..2b0e07a 100644 --- a/prod/index.html +++ b/prod/index.html @@ -3,9 +3,11 @@ Your app name + + -
- +
+ \ No newline at end of file diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css new file mode 100644 index 0000000..2ae1ec2 --- /dev/null +++ b/src/components/Header/Header.css @@ -0,0 +1,12 @@ +.header h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} \ No newline at end of file diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 8f9b898..b529af5 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,10 +1,12 @@ import React, { Component } from 'react'; import LargeInput from './../LargeInput/LargeInput' +require("./Header.css"); + export default class Header extends Component { render() { return ( -
+

todos

diff --git a/src/components/LargeInput/LargeInput.css b/src/components/LargeInput/LargeInput.css new file mode 100644 index 0000000..8d47e8a --- /dev/null +++ b/src/components/LargeInput/LargeInput.css @@ -0,0 +1,25 @@ +.new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} \ No newline at end of file diff --git a/src/components/LargeInput/LargeInput.js b/src/components/LargeInput/LargeInput.js index 6c5084b..379bf88 100644 --- a/src/components/LargeInput/LargeInput.js +++ b/src/components/LargeInput/LargeInput.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +require("./LargeInput.css"); export default class LargeInput extends Component { @@ -10,7 +11,7 @@ export default class LargeInput extends Component { render() { return ( - + ); } } \ No newline at end of file diff --git a/src/components/Todo/Todo.css b/src/components/Todo/Todo.css new file mode 100644 index 0000000..81ef9a7 --- /dev/null +++ b/src/components/Todo/Todo.css @@ -0,0 +1,97 @@ +.todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +.todo-list li:last-child { + border-bottom: none; +} + +.todo-list li.editing { + border-bottom: none; + padding: 0; +} + +.todo-list li.editing .edit { + display: block; + width: 506px; + padding: 12px 16px; + margin: 0 0 0 43px; +} + +.todo-list li.editing .view { + display: none; +} + +.todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; /* Mobile Safari */ + -webkit-appearance: none; + appearance: none; +} + +.todo-list li .toggle:after { + content: url('data:image/svg+xml;utf8,'); +} + +.todo-list li .toggle:checked:after { + content: url('data:image/svg+xml;utf8,'); +} + +.todo-list li label { + word-break: break-all; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +.todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +.todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +.todo-list li .destroy:hover { + color: #af5b5e; + cursor: pointer; +} + +.todo-list li .destroy:after { + content: '×'; +} + +.todo-list li:hover .destroy { + display: block; +} + +.todo-list li .edit { + display: none; +} + +.todo-list li.editing:last-child { + margin-bottom: -1px; +} \ No newline at end of file diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index 8ec2726..d69e586 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -1,9 +1,21 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; +require("./Todo.css"); export default class Todo extends Component { + + toggleTodo() { + this.props.toggleTodo(this.props.id); + } + render() { return ( -
Todo
+
  • +
    + + + +
    +
  • ); } } \ No newline at end of file diff --git a/src/components/TodoApp/TodoApp.css b/src/components/TodoApp/TodoApp.css new file mode 100644 index 0000000..6d62595 --- /dev/null +++ b/src/components/TodoApp/TodoApp.css @@ -0,0 +1,102 @@ +html, +body { + margin: 0; + padding: 0; + background-color: #f5f5f5; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #f5f5f5; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 300; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + + +.todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +:focus { + outline: 0; +} + +.hidden { + display: none; +} + +.todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + .toggle-all, + .todo-list li .toggle { + background: none; + } + + .todo-list li .toggle { + height: 40px; + } + + .toggle-all { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +@media (max-width: 430px) { + .footer { + height: 50px; + } + + .filters { + bottom: 10px; + } +} \ No newline at end of file diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 61109ef..284675d 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -1,5 +1,7 @@ import React, { Component } from 'react'; import Header from './../Header/Header' +import TodoList from './../TodoList/TodoList' +require("./TodoApp.css"); export default class Todo extends Component { @@ -7,30 +9,41 @@ export default class Todo extends Component { super(props); this.state = { - todos: [], + todos: [{title : 'Artem'}], inputText: "" }; this.onTextChange = this.onTextChange.bind(this); this.onAddTodo = this.onAddTodo.bind(this); + this.toggleTodo = this.toggleTodo.bind(this); } onTextChange(event) { this.setState({ - text: event.target.value + text: event.target.value, + completed: false }); - console.log(event); } onAddTodo(event) { - console.log(event); + const newTodo = {'title' : event.target.value}; + console.log(newTodo); + this.setState((prevState) => ({ + todos: prevState.todos.concat(newTodo), + inputText: "" + })); + } + + toggleTodo(id) { + console.log(id); } render() { return ( -
    -
    -
    +
    +
    + +
    ); } } \ No newline at end of file diff --git a/src/components/TodoList/TodoList.css b/src/components/TodoList/TodoList.css new file mode 100644 index 0000000..75298e0 --- /dev/null +++ b/src/components/TodoList/TodoList.css @@ -0,0 +1,40 @@ +.main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +/*Toggle all checkbox*/ + +label[for='toggle-all'] { + display: none; +} + +.toggle-all { + position: absolute; + top: -55px; + left: -12px; + width: 60px; + height: 34px; + text-align: center; + border: none; /* Mobile Safari */ +} + +.toggle-all:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +.toggle-all:checked:before { + color: #737373; +} + +/*Todo list*/ + +.todo-list { + margin: 0; + padding: 0; + list-style: none; +} \ No newline at end of file diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js index 1752d45..d67218a 100644 --- a/src/components/TodoList/TodoList.js +++ b/src/components/TodoList/TodoList.js @@ -1,15 +1,21 @@ import React, {Component} from 'react'; +import Todo from './../Todo/Todo' +require("./TodoList.css"); export default class TodoList extends Component { render() { return ( -
    +
    - {this.props.items.map(item => ( - - ))} + -
    +
    + {this.props.todos.map(item => ( + + ))} +
    + + ); } } \ No newline at end of file diff --git a/src/utils/GUID.js b/src/utils/GUID.js new file mode 100644 index 0000000..36f8378 --- /dev/null +++ b/src/utils/GUID.js @@ -0,0 +1,16 @@ +/** + * Created by tema on 05.12.16. + */ + +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +} + +module.exports = guid; diff --git a/webpack.config.js b/webpack.config.js index d0e698e..ed0c574 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,5 @@ +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + module.exports = { entry: { javascript: "./src/js/app.js", @@ -23,7 +25,15 @@ module.exports = { { test: /\.html$/, loader: "file?name=[name].[ext]", + }, + { + test: /\.css$/, + loader: ExtractTextPlugin.extract("style-loader", "css-loader") } ] - } + }, + // Use the plugin to specify the resulting filename (and add needed behavior to the compiler) + plugins: [ + new ExtractTextPlugin("./styles.css") + ] }; \ No newline at end of file From 44f2b573a6bb1ede917a2a1b873d11439a618eda Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 15:43:07 +0300 Subject: [PATCH 05/16] Add completed behavior --- src/components/Todo/Todo.js | 8 ++++---- src/components/TodoApp/TodoApp.js | 29 ++++++++++++++++++++--------- src/components/TodoList/TodoList.js | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index d69e586..a6ea1ad 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -3,15 +3,15 @@ require("./Todo.css"); export default class Todo extends Component { - toggleTodo() { - this.props.toggleTodo(this.props.id); + toggleTodo(e) { + this.props.toggleTodo(e, this.props.id); } render() { return ( -
  • +
  • - +
    diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 284675d..75b09b0 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -1,6 +1,7 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import Header from './../Header/Header' import TodoList from './../TodoList/TodoList' +import guid from './../../utils/GUID'; require("./TodoApp.css"); export default class Todo extends Component { @@ -9,7 +10,7 @@ export default class Todo extends Component { super(props); this.state = { - todos: [{title : 'Artem'}], + todos: [], inputText: "" }; @@ -20,29 +21,39 @@ export default class Todo extends Component { onTextChange(event) { this.setState({ - text: event.target.value, - completed: false + inputText: event.target.value }); } onAddTodo(event) { - const newTodo = {'title' : event.target.value}; - console.log(newTodo); + const newTodo = { + title: event.target.value, + completed: false, + id : guid() + }; this.setState((prevState) => ({ todos: prevState.todos.concat(newTodo), inputText: "" })); } - toggleTodo(id) { - console.log(id); + toggleTodo(event, id) { + var updatedItems = this.state.todos.map(item => { + if (id === item.id) + item.completed = event.target.checked; + return item; + }); + this.setState({ + todos: [].concat(updatedItems) + }); + console.log(this.state); } render() { return (
    - +
    ); } diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js index d67218a..44cf447 100644 --- a/src/components/TodoList/TodoList.js +++ b/src/components/TodoList/TodoList.js @@ -11,7 +11,7 @@ export default class TodoList extends Component {
    {this.props.todos.map(item => ( - + ))}
    From bdb4624e8e76fb7c271740cbef14312329f02f13 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 15:45:24 +0300 Subject: [PATCH 06/16] Fix clear input text, after add todo --- src/components/Header/Header.js | 2 +- src/components/TodoApp/TodoApp.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index b529af5..2c16657 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -8,7 +8,7 @@ export default class Header extends Component { return (

    todos

    - +
    ); } diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 75b09b0..6f2654c 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -11,7 +11,7 @@ export default class Todo extends Component { this.state = { todos: [], - inputText: "" + inputText: "aaa" }; this.onTextChange = this.onTextChange.bind(this); From 84049eb8681ec2b0f060facdb064251237045b7b Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 15:53:52 +0300 Subject: [PATCH 07/16] Add delete todo behaviour --- src/components/Todo/Todo.js | 8 ++++++-- src/components/TodoApp/TodoApp.js | 27 +++++++++++++++++++-------- src/components/TodoList/TodoList.js | 3 ++- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index a6ea1ad..15aec54 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -4,7 +4,11 @@ require("./Todo.css"); export default class Todo extends Component { toggleTodo(e) { - this.props.toggleTodo(e, this.props.id); + this.props.onToggleTodo(e, this.props.id); + } + + deleteTodo(e) { + this.props.onDeleteItem(e, this.props.id); } render() { @@ -13,7 +17,7 @@ export default class Todo extends Component {
    - +
  • ); diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 6f2654c..127d018 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -11,12 +11,13 @@ export default class Todo extends Component { this.state = { todos: [], - inputText: "aaa" + inputText: "" }; this.onTextChange = this.onTextChange.bind(this); this.onAddTodo = this.onAddTodo.bind(this); - this.toggleTodo = this.toggleTodo.bind(this); + this.onToggleTodo = this.onToggleTodo.bind(this); + this.onDeleteItem = this.onDeleteItem.bind(this); } onTextChange(event) { @@ -37,11 +38,11 @@ export default class Todo extends Component { })); } - toggleTodo(event, id) { - var updatedItems = this.state.todos.map(item => { - if (id === item.id) - item.completed = event.target.checked; - return item; + onToggleTodo(event, id) { + const updatedItems = this.state.todos.map(todo => { + if (id === todo.id) + todo.completed = event.target.checked; + return todo; }); this.setState({ todos: [].concat(updatedItems) @@ -49,11 +50,21 @@ export default class Todo extends Component { console.log(this.state); } + onDeleteItem(event, id) { + var updatedItems = this.state.todos.filter(todo => { + return todo.id !== id; + }); + + this.setState({ + todos: [].concat(updatedItems) + }); + } + render() { return (
    - +
    ); } diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js index 44cf447..75d74fa 100644 --- a/src/components/TodoList/TodoList.js +++ b/src/components/TodoList/TodoList.js @@ -11,7 +11,8 @@ export default class TodoList extends Component {
    {this.props.todos.map(item => ( - + ))}
    From 60e5287bf06aedae55e9f40d1b1462b1b257971f Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 16:35:53 +0300 Subject: [PATCH 08/16] Add toggle all --- src/components/Todo/Todo.js | 2 +- src/components/TodoApp/TodoApp.js | 39 +++++++++++++++++++++++------ src/components/TodoList/TodoList.js | 2 +- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index 15aec54..a4447c7 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -15,7 +15,7 @@ export default class Todo extends Component { return (
  • - +
    diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 127d018..4041367 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -11,13 +11,15 @@ export default class Todo extends Component { this.state = { todos: [], - inputText: "" + inputText: "", + toggledAll: false }; this.onTextChange = this.onTextChange.bind(this); this.onAddTodo = this.onAddTodo.bind(this); this.onToggleTodo = this.onToggleTodo.bind(this); this.onDeleteItem = this.onDeleteItem.bind(this); + this.onToggleAll = this.onToggleAll.bind(this); } onTextChange(event) { @@ -34,7 +36,8 @@ export default class Todo extends Component { }; this.setState((prevState) => ({ todos: prevState.todos.concat(newTodo), - inputText: "" + inputText: "", + toggledAll: false })); } @@ -45,26 +48,46 @@ export default class Todo extends Component { return todo; }); this.setState({ - todos: [].concat(updatedItems) - }); - console.log(this.state); + todos: [].concat(updatedItems), + }, this.checkToggleAll); } onDeleteItem(event, id) { var updatedItems = this.state.todos.filter(todo => { return todo.id !== id; }); + this.setState({ + todos: [].concat(updatedItems), + toggledAll: this.checkToggleAll() + }, this.checkToggleAll); + } + + onToggleAll(event) { + const updatedItems = this.state.todos.map(todo => { + todo.completed = !this.state.toggledAll; + return todo; + }); + this.setState({ + todos: [].concat(updatedItems), + toggledAll: !this.state.toggledAll + }); + } + checkToggleAll() { + const val = this.state.todos.length !== 0 && + this.state.todos.filter(todo => { return todo.completed !== true; }).length === 0; this.setState({ - todos: [].concat(updatedItems) + toggledAll: val }); } render() { return (
    -
    - +
    +
    ); } diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js index 75d74fa..2aed1a0 100644 --- a/src/components/TodoList/TodoList.js +++ b/src/components/TodoList/TodoList.js @@ -7,7 +7,7 @@ export default class TodoList extends Component { return (
    - +
    {this.props.todos.map(item => ( From 94509b3289bf39ca8eab87d7cacedb043189522d Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 16:50:32 +0300 Subject: [PATCH 09/16] Add footer, count items left --- src/components/Footer/Footer.css | 74 +++++++++++++++++++++++++++++++ src/components/Footer/Footer.js | 26 +++++++++++ src/components/TodoApp/TodoApp.js | 21 +++++---- 3 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 src/components/Footer/Footer.css create mode 100644 src/components/Footer/Footer.js diff --git a/src/components/Footer/Footer.css b/src/components/Footer/Footer.css new file mode 100644 index 0000000..d73b8dc --- /dev/null +++ b/src/components/Footer/Footer.css @@ -0,0 +1,74 @@ +.footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +.footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +.todo-count { + float: left; + text-align: left; +} + +.todo-count strong { + font-weight: 300; +} + +.filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +.filters li { + display: inline; +} + +.filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +.filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +.filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +.clear-completed, +html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; +} + +.clear-completed:hover { + text-decoration: underline; +} diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js new file mode 100644 index 0000000..c75b20f --- /dev/null +++ b/src/components/Footer/Footer.js @@ -0,0 +1,26 @@ +import React, {Component} from 'react'; +require("./Footer.css"); + + +export default class Footer extends Component { + render() { + return ( +
    + {this.props.itemsLeft} items left + + +
    + + ); + } +} \ No newline at end of file diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 4041367..3df8cd0 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import Header from './../Header/Header' import TodoList from './../TodoList/TodoList' +import Footer from './../Footer/Footer' import guid from './../../utils/GUID'; require("./TodoApp.css"); @@ -12,7 +13,8 @@ export default class Todo extends Component { this.state = { todos: [], inputText: "", - toggledAll: false + toggledAll: false, + itemsLeft: 0 }; this.onTextChange = this.onTextChange.bind(this); @@ -38,7 +40,7 @@ export default class Todo extends Component { todos: prevState.todos.concat(newTodo), inputText: "", toggledAll: false - })); + }), this.checkToggles); } onToggleTodo(event, id) { @@ -49,7 +51,7 @@ export default class Todo extends Component { }); this.setState({ todos: [].concat(updatedItems), - }, this.checkToggleAll); + }, this.checkToggles); } onDeleteItem(event, id) { @@ -59,7 +61,7 @@ export default class Todo extends Component { this.setState({ todos: [].concat(updatedItems), toggledAll: this.checkToggleAll() - }, this.checkToggleAll); + }, this.checkToggles); } onToggleAll(event) { @@ -70,14 +72,14 @@ export default class Todo extends Component { this.setState({ todos: [].concat(updatedItems), toggledAll: !this.state.toggledAll - }); + }, this.checkToggles); } - checkToggleAll() { - const val = this.state.todos.length !== 0 && - this.state.todos.filter(todo => { return todo.completed !== true; }).length === 0; + checkToggles() { + const itemsLeft = this.state.todos.filter(todo => { return todo.completed !== true; }).length; this.setState({ - toggledAll: val + toggledAll: this.state.todos.length !== 0 && itemsLeft === 0, + itemsLeft: itemsLeft }); } @@ -88,6 +90,7 @@ export default class Todo extends Component { onAddTodo={this.onAddTodo} /> +
    ); } From 36efbb997ecea329ef9170b00b8f66edf8df448e Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 16:59:37 +0300 Subject: [PATCH 10/16] Add filters switch --- src/components/Footer/Footer.js | 19 ++++++++++++++++--- src/components/TodoApp/TodoApp.js | 13 +++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index c75b20f..d3f7f71 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -3,19 +3,32 @@ require("./Footer.css"); export default class Footer extends Component { + + showAll() { + this.props.setFilter('all'); + } + + showActive() { + this.props.setFilter('active'); + } + + showCompleted() { + this.props.setFilter('completed'); + } + render() { return (
    {this.props.itemsLeft} items left diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 3df8cd0..2be1a78 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -14,7 +14,8 @@ export default class Todo extends Component { todos: [], inputText: "", toggledAll: false, - itemsLeft: 0 + itemsLeft: 0, + filter: 'all' }; this.onTextChange = this.onTextChange.bind(this); @@ -22,6 +23,7 @@ export default class Todo extends Component { this.onToggleTodo = this.onToggleTodo.bind(this); this.onDeleteItem = this.onDeleteItem.bind(this); this.onToggleAll = this.onToggleAll.bind(this); + this.setFilter = this.setFilter.bind(this); } onTextChange(event) { @@ -83,6 +85,12 @@ export default class Todo extends Component { }); } + setFilter(filter) { + this.setState({ + filter: filter + }); + } + render() { return (
    @@ -90,7 +98,8 @@ export default class Todo extends Component { onAddTodo={this.onAddTodo} /> -
    +
    ); } From 62d0e2e030aa68e5b288c0dba2d653db8fc13d6b Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 17:03:49 +0300 Subject: [PATCH 11/16] Add sorting of todos by filter --- src/components/TodoApp/TodoApp.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 2be1a78..83beb2d 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -91,14 +91,32 @@ export default class Todo extends Component { }); } + filterTodos() { + switch(this.state.filter) { + case 'all': + return this.state.todos; + case 'active': + return this.state.todos.filter(todo => { return todo.completed !== true}); + case 'completed': + return this.state.todos.filter(todo => { return todo.completed === true}); + } + } + render() { return (
    -
    - -
    + +
    ); From 313ab46a1b58a0036e2f58f97d153477001fd371 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 17:06:18 +0300 Subject: [PATCH 12/16] Add clear completed behaviour --- src/components/Footer/Footer.js | 2 +- src/components/TodoApp/TodoApp.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index d3f7f71..d4ddbe6 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -31,7 +31,7 @@ export default class Footer extends Component { Completed
  • - + ); diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index 83beb2d..adfa532 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -24,6 +24,7 @@ export default class Todo extends Component { this.onDeleteItem = this.onDeleteItem.bind(this); this.onToggleAll = this.onToggleAll.bind(this); this.setFilter = this.setFilter.bind(this); + this.clearCompleted = this.clearCompleted.bind(this); } onTextChange(event) { @@ -62,7 +63,6 @@ export default class Todo extends Component { }); this.setState({ todos: [].concat(updatedItems), - toggledAll: this.checkToggleAll() }, this.checkToggles); } @@ -73,7 +73,6 @@ export default class Todo extends Component { }); this.setState({ todos: [].concat(updatedItems), - toggledAll: !this.state.toggledAll }, this.checkToggles); } @@ -102,6 +101,15 @@ export default class Todo extends Component { } } + clearCompleted() { + var updatedItems = this.state.todos.filter(todo => { + return todo.completed !== true; + }); + this.setState({ + todos: [].concat(updatedItems), + }, this.checkToggles); + } + render() { return (
    @@ -117,7 +125,8 @@ export default class Todo extends Component {
    + setFilter={this.setFilter} + clearCompleted={this.clearCompleted}/>
    ); } From ddc10e3fed8b7407b0727f61955d9d1145912980 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 17:14:09 +0300 Subject: [PATCH 13/16] Add save state to local storage --- src/components/TodoApp/TodoApp.js | 45 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index adfa532..a08f2b6 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -10,13 +10,16 @@ export default class Todo extends Component { constructor(props) { super(props); - this.state = { - todos: [], - inputText: "", - toggledAll: false, - itemsLeft: 0, - filter: 'all' - }; + + const loadState = JSON.parse(localStorage.getItem('todos')); + this.state = loadState || { + todos: [], + inputText: "", + toggledAll: false, + itemsLeft: 0, + filter: 'all' + }; + this.onTextChange = this.onTextChange.bind(this); this.onAddTodo = this.onAddTodo.bind(this); @@ -30,14 +33,14 @@ export default class Todo extends Component { onTextChange(event) { this.setState({ inputText: event.target.value - }); + }, this.saveState); } onAddTodo(event) { const newTodo = { title: event.target.value, completed: false, - id : guid() + id: guid() }; this.setState((prevState) => ({ todos: prevState.todos.concat(newTodo), @@ -77,27 +80,33 @@ export default class Todo extends Component { } checkToggles() { - const itemsLeft = this.state.todos.filter(todo => { return todo.completed !== true; }).length; + const itemsLeft = this.state.todos.filter(todo => { + return todo.completed !== true; + }).length; this.setState({ toggledAll: this.state.todos.length !== 0 && itemsLeft === 0, itemsLeft: itemsLeft - }); + }, this.saveState); } setFilter(filter) { this.setState({ filter: filter - }); + }, this.saveState); } filterTodos() { - switch(this.state.filter) { + switch (this.state.filter) { case 'all': return this.state.todos; case 'active': - return this.state.todos.filter(todo => { return todo.completed !== true}); + return this.state.todos.filter(todo => { + return todo.completed !== true + }); case 'completed': - return this.state.todos.filter(todo => { return todo.completed === true}); + return this.state.todos.filter(todo => { + return todo.completed === true + }); } } @@ -110,12 +119,16 @@ export default class Todo extends Component { }, this.checkToggles); } + saveState() { + localStorage.setItem('todos', JSON.stringify(this.state)); + } + render() { return (
    + onAddTodo={this.onAddTodo}/> Date: Sun, 11 Dec 2016 17:23:53 +0300 Subject: [PATCH 14/16] Add express, fix css and js production routes --- index.js | 16 ++++++++++++++++ package.json | 4 +++- prod/index.html | 4 ++-- webpack-prod.config.js | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 index.js create mode 100644 webpack-prod.config.js diff --git a/index.js b/index.js new file mode 100644 index 0000000..ba8d9cb --- /dev/null +++ b/index.js @@ -0,0 +1,16 @@ +/** + * Created by tema on 27.11.16. + */ + +const express = require('express'); + +const app = express(); + +app.use('/', express.static(`${__dirname}/prod`)); +app.use('/css', express.static(`${__dirname}/prod/css`)); +app.use('/js', express.static(`${__dirname}/prod/js`)); + +const port = process.env.PORT || 5000; +app.listen(port, () => { + console.log(`Listening on ${port}`); +}); diff --git a/package.json b/package.json index 8fa37fa..75b77c0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot --inline --history-api-fallback" + "dev": "node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot --inline --history-api-fallback", + "postinstall": "webpack --config ./webpack-prod.config.js --progress --colors", + "start": "node index.js" }, "repository": { "type": "git", diff --git a/prod/index.html b/prod/index.html index 2b0e07a..6c43c0a 100644 --- a/prod/index.html +++ b/prod/index.html @@ -3,11 +3,11 @@ Your app name - +
    - + \ No newline at end of file diff --git a/webpack-prod.config.js b/webpack-prod.config.js new file mode 100644 index 0000000..38952a1 --- /dev/null +++ b/webpack-prod.config.js @@ -0,0 +1,39 @@ +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + +module.exports = { + entry: { + javascript: "./src/js/app.js", + html: "./prod/index.html", + }, + + output: { + filename: "/js/app.js", + path: "./prod", + }, + + resolve: { + extensions: ['', '.js', '.jsx', '.json'] + }, + + module: { + loaders: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + loaders: ["react-hot", "babel-loader"], + }, + { + test: /\.html$/, + loader: "file?name=[name].[ext]", + }, + { + test: /\.css$/, + loader: ExtractTextPlugin.extract("style-loader", "css-loader") + } + ] + }, + // Use the plugin to specify the resulting filename (and add needed behavior to the compiler) + plugins: [ + new ExtractTextPlugin("./css/styles.css") + ] +}; \ No newline at end of file From cf122c1693ed2307b23d7f17a0642c18ee30699f Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 17:33:15 +0300 Subject: [PATCH 15/16] Change dependencies for heroic --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 75b77c0..21777ad 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,7 @@ "react-dom": "^15.4.1", "todomvc-app-css": "^2.0.6", "todomvc-common": "^1.0.3", - "webpack": "^1.14.0" - }, - "devDependencies": { + "webpack": "^1.14.0", "babel-core": "^6.20.0", "babel-loader": "^6.2.9", "babel-preset-es2015": "^6.18.0", @@ -21,7 +19,9 @@ "file-loader": "^0.9.0", "jsx-loader": "^0.13.2", "react-hot-loader": "^1.3.1", - "style-loader": "^0.13.1", + "style-loader": "^0.13.1" + }, + "devDependencies": { "webpack-dev-server": "^1.16.2" }, "scripts": { From 8315f27d4f8f0c359663555df3141711d44ed84b Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Sun, 11 Dec 2016 22:08:53 +0300 Subject: [PATCH 16/16] Add edit todo function --- src/components/Todo/Todo.js | 24 +++++++++++++++--- src/components/TodoApp/TodoApp.js | 38 ++++++++++++++++++++++++++--- src/components/TodoList/TodoList.js | 11 +++++++-- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/components/Todo/Todo.js b/src/components/Todo/Todo.js index a4447c7..9d13fa6 100644 --- a/src/components/Todo/Todo.js +++ b/src/components/Todo/Todo.js @@ -11,14 +11,32 @@ export default class Todo extends Component { this.props.onDeleteItem(e, this.props.id); } + editTodo(e) { + this.props.editTodo(e, this.props.id); + } + + saveTodo(e) { + if (e.keyCode == 13) { + this.props.saveTodo(e, this.props.id, e.target.value); + } + } + + componentDidUpdate(prevProps) { + var node = this.refs.editField; + node.focus(); + } + render() { return ( -
  • +
  • - - + +
    +
  • ); } diff --git a/src/components/TodoApp/TodoApp.js b/src/components/TodoApp/TodoApp.js index a08f2b6..8a97466 100644 --- a/src/components/TodoApp/TodoApp.js +++ b/src/components/TodoApp/TodoApp.js @@ -10,7 +10,6 @@ export default class Todo extends Component { constructor(props) { super(props); - const loadState = JSON.parse(localStorage.getItem('todos')); this.state = loadState || { todos: [], @@ -20,7 +19,6 @@ export default class Todo extends Component { filter: 'all' }; - this.onTextChange = this.onTextChange.bind(this); this.onAddTodo = this.onAddTodo.bind(this); this.onToggleTodo = this.onToggleTodo.bind(this); @@ -28,6 +26,8 @@ export default class Todo extends Component { this.onToggleAll = this.onToggleAll.bind(this); this.setFilter = this.setFilter.bind(this); this.clearCompleted = this.clearCompleted.bind(this); + this.editTodo = this.editTodo.bind(this); + this.saveTodo = this.saveTodo.bind(this); } onTextChange(event) { @@ -40,7 +40,8 @@ export default class Todo extends Component { const newTodo = { title: event.target.value, completed: false, - id: guid() + id: guid(), + editable: false }; this.setState((prevState) => ({ todos: prevState.todos.concat(newTodo), @@ -123,6 +124,32 @@ export default class Todo extends Component { localStorage.setItem('todos', JSON.stringify(this.state)); } + editTodo(event, id) { + const updatedItems = this.state.todos.map(todo => { + if (id === todo.id) + todo.editable = true; + else + todo.editable = false; + return todo; + }); + this.setState({ + todos: [].concat(updatedItems), + }, this.checkToggles); + } + + saveTodo(event, id, text) { + const updatedItems = this.state.todos.map(todo => { + if (id === todo.id) { + todo.title = text; + todo.editable = false; + } + return todo; + }); + this.setState({ + todos: [].concat(updatedItems), + }, this.checkToggles); + } + render() { return (
    @@ -134,7 +161,10 @@ export default class Todo extends Component { toggledAll={this.state.toggledAll} onToggleTodo={this.onToggleTodo} onDeleteItem={this.onDeleteItem} - onToggleAll={this.onToggleAll}/> + onToggleAll={this.onToggleAll} + editTodo={this.editTodo} + saveTodo={this.saveTodo} + />