diff --git a/package.json b/package.json index bc825e78a..1d8b43708 100755 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "author": "Mate Academy", "license": "GPL-3.0", "dependencies": { + "classnames": "^2.2.6", "prop-types": "^15.7.2", "react": "^16.8.6", "react-dom": "^16.8.6", diff --git a/src/App.js b/src/App.js index 33f1939ef..4f6124e94 100644 --- a/src/App.js +++ b/src/App.js @@ -1,75 +1,147 @@ import React from 'react'; +import getTodos from './api/todos'; +import TodoApp from './TodoApp'; +import TodoList from './TodoList'; +import TodosFilter from './TodosFilter'; -function App() { - return ( -
-
-

todos

+class App extends React.Component { + state = { + visibleTodos: [], + currentFilter: 'all', + }; - -
- -
- - - - -
+ componentDidMount() { + this.setState({ + visibleTodos: getTodos, + }); + } + + addTodo = (title) => { + this.setState(prevState => ({ + visibleTodos: [ + ...prevState.visibleTodos, + { + title, + id: Math.ceil(Math.random() * 1000 + prevState.visibleTodos.length), + completed: false, + }, + ], + })); + }; + + handlerChangeCompleted = (id) => { + this.setState(prevState => ({ + visibleTodos: prevState.visibleTodos.map((todo) => { + if (todo.id === id) { + return { + ...todo, + completed: !todo.completed, + }; + } + + return todo; + }), + })); + }; + + handleChangeCompletedAll = () => { + this.setState(prevState => { + if (prevState.visibleTodos.every(todo => !todo.completed) + || prevState.visibleTodos.every(todo => todo.completed)) { + return { + visibleTodos: prevState.visibleTodos.map(todo => ({ + ...todo, + completed: !todo.completed, + })), + }; + } else { + return { + visibleTodos: prevState.visibleTodos.map(todo => ({ + ...todo, + completed: true, + })), + }; + } + }); + }; + + handleRemoveTodo = (id) => { + this.setState(prevState => ({ + visibleTodos: prevState.visibleTodos.filter(todo => ( + todo.id !== id + )), + } + )); + }; + + handlerFilter = () => { + const { currentFilter, visibleTodos } = this.state; + + switch (currentFilter) { + case 'all': + return visibleTodos; + case 'active': + return visibleTodos.filter(todo => ( + !todo.completed)); + case 'completed': + return visibleTodos.filter(todo => ( + todo.completed)); + } + }; - -
- ); + + + + ); + } } export default App; diff --git a/src/Todo.js b/src/Todo.js new file mode 100644 index 000000000..4836f5e28 --- /dev/null +++ b/src/Todo.js @@ -0,0 +1,33 @@ +import React from 'react'; + +class Todo extends React.Component { + render() { + const { todo, changeCompleted, removeTodo} = this.props; + + return ( +
+ changeCompleted(todo.id)} + /> + +
+ ); + } + +} + +export default Todo; diff --git a/src/TodoApp.js b/src/TodoApp.js new file mode 100644 index 000000000..1186760c9 --- /dev/null +++ b/src/TodoApp.js @@ -0,0 +1,50 @@ +import React from 'react'; +import propTypes from 'prop-types'; + +class TodoApp extends React.Component { + state = { + title: '', + }; + + onChangeInput = (event) => { + const { value } = event.target; + + this.setState({ title: value }); + }; + + onCLickEnter = (event) => { + const { title } = this.state; + if (event.keyCode === 13) { + event.preventDefault(); + + if (!title || title === ' ') { + return; + } + + this.props.addTodo(title); + this.setState({ title: '' }); + } + }; + + render() { + const { title } = this.state; + + return ( + + ); + } +} + +TodoApp.propTypes = { + addTodo: propTypes.func.isRequired, +}; + +export default TodoApp; diff --git a/src/TodoList.js b/src/TodoList.js new file mode 100644 index 000000000..590c271eb --- /dev/null +++ b/src/TodoList.js @@ -0,0 +1,50 @@ +import React from 'react'; +import propTypes from 'prop-types'; +import classnames from 'classnames'; +import './styles/todoList.css'; +import Todo from './Todo'; + +const TodoList = ({ todos, changeCompleted, changeCompletedAll, removeTodo}) => ( +
+ changeCompletedAll()} + /> + + + +
+); + +TodoList.propTypes = { + todos: propTypes.arrayOf().isRequired, + changeCompleted: propTypes.func, + changeCompletedAll: propTypes.func, +}; + +export default TodoList; diff --git a/src/TodosFilter.js b/src/TodosFilter.js new file mode 100644 index 000000000..96a3e5097 --- /dev/null +++ b/src/TodosFilter.js @@ -0,0 +1,35 @@ +import React from 'react'; +import propTypes from 'prop-types'; + +const TodosFilter = ({ handlerFilter }) => { + + return ( + + ); +}; + +TodosFilter.propTypes = { + filterAll: propTypes.func, + filterCompleted: propTypes.func, + filterActive: propTypes.func, +}; + +export default TodosFilter; diff --git a/src/api/todos.js b/src/api/todos.js new file mode 100644 index 000000000..3e5897268 --- /dev/null +++ b/src/api/todos.js @@ -0,0 +1,19 @@ +const todos = [ + { + id: 1, + title: 'Learn HTML', + completed: false, + }, + { + id: 2, + title: 'Learn CSS', + completed: false, + }, + { + id: 3, + title: 'Learn JS', + completed: false, + }, +]; + +export default todos; diff --git a/src/index.js b/src/index.js index ebde5cecc..e31e42b12 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './base.css'; -import './index.css'; +import './styles/base.css'; +import './styles/index.css'; import App from './App'; ReactDOM.render( diff --git a/src/base.css b/src/styles/base.css similarity index 100% rename from src/base.css rename to src/styles/base.css diff --git a/src/index.css b/src/styles/index.css similarity index 100% rename from src/index.css rename to src/styles/index.css diff --git a/src/styles/todoList.css b/src/styles/todoList.css new file mode 100644 index 000000000..ca972a794 --- /dev/null +++ b/src/styles/todoList.css @@ -0,0 +1,11 @@ +main { + display: block; +} + +.todo-completed { + text-decoration: line-through; +} + +.filters--active:focus { + border-color: rgba(175, 47, 47, 0.2); +}