diff --git a/README.md b/README.md index dbb87e138..8c49628b1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # React ToDo App -- Replace `` with your Github username in the [DEMO LINK](https://.github.io/react_todo-app/) +- Replace `` with your Github username in the [DEMO LINK](https://alenaloik.github.io/react_todo-app/) - Follow the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline) ## Description diff --git a/src/App.js b/src/App.js index 13ea9e061..ed8b45d35 100644 --- a/src/App.js +++ b/src/App.js @@ -1,85 +1,144 @@ -import React from 'react'; +import React, { Component } from 'react'; +import { TodoList } from './components/TodoList/TodoList'; +import NewTodo from './components/NewTodo/NewTodo'; +import { TodosFilter } from './components/TodosFilter/TodosFilter'; -function App() { - return ( -
-
-

todos

+class App extends Component { + state = { + todos: [], + showParam: 'all', + }; - -
+ addTodo = (todo) => { + this.setState(prevState => ({ + todos: [...prevState.todos, todo], + })); + } -
- - + updateTodosToShow = (todoToShow) => { + this.setState({ showParam: todoToShow }); + } -
    -
  • -
    - - -
    - -
  • + handleRemuve = (id) => { + this.setState(prevState => ({ + todos: prevState.todos.filter(todo => ( + todo.id !== id + )), + })); + } -
  • -
    - - -
    - -
  • + handleRemuveCompleted = () => { + this.setState(prevState => ({ + todos: prevState.todos.filter(todo => ( + !todo.completed + )), + })); + } -
  • -
    - - -
    - -
  • + toggleComplete = (id) => { + this.setState(prevState => ({ + todos: prevState.todos.map((todo) => { + if (todo.id === id) { + return { + ...todo, + completed: !todo.completed, + }; + } -
  • -
    - - -
    - -
  • -
-
+ return todo; + }), + })); + } -
- - 3 items left - + toggleCompleteAll = () => { + if (this.state.todos.every(todo => (todo.completed))) { + this.setState(prevState => ({ + todos: prevState.todos.map(todo => ({ + ...todo, + completed: false, + })), + })); + } else { + this.setState(prevState => ({ + todos: prevState.todos.map(todo => ({ + ...todo, + completed: true, + })), + })); + } + } -
    -
  • - All -
  • + render() { + let todoView = []; + const itemLeft = this.state.todos.filter(todo => ( + !todo.completed)).length; -
  • - Active -
  • + switch (this.state.showParam) { + case 'active': + todoView = [...this.state.todos].filter(todo => ( + !todo.completed + )); + break; + case 'completed': + todoView = [...this.state.todos].filter(todo => ( + todo.completed + )); + break; + default: + todoView = [...this.state.todos]; + } -
  • - Completed -
  • -
+ return ( +
+
+

todos

+ +
+ {(this.state.todos.length) + ? ( + <> +
+ + + +
- -
-
- ); +
+ + {itemLeft} + item left + + + { + (this.state.todos.filter(todo => ( + todo.completed)).length) + ? ( + + ) + : ('') + } +
+ + ) : ''} + + ); + } } export default App; diff --git a/src/components/NewTodo/NewTodo.css b/src/components/NewTodo/NewTodo.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/NewTodo/NewTodo.js b/src/components/NewTodo/NewTodo.js new file mode 100644 index 000000000..32777a399 --- /dev/null +++ b/src/components/NewTodo/NewTodo.js @@ -0,0 +1,49 @@ +import React, { Component } from 'react'; +import './NewTodo.css'; +import PropTypes from 'prop-types'; + +class NewTodo extends Component { + state = { + content: '', + id: 1, + completed: false, + } + + handleChange = (event) => { + this.setState({ + content: event.target.value.trim(), + }); + } + + handleSubmit = (e) => { + e.preventDefault(); + + if (this.state.content) { + this.props.addTodo(this.state); + + this.setState(prevState => ({ + content: '', + id: prevState.id + 1, + })); + } + } + + render() { + return ( +
+ +
+ ); + } +} + +export default NewTodo; + +NewTodo.propTypes = { + addTodo: PropTypes.func.isRequired, +}; diff --git a/src/components/TodoList/TodoList.css b/src/components/TodoList/TodoList.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/TodoList/TodoList.js b/src/components/TodoList/TodoList.js new file mode 100644 index 000000000..e00392016 --- /dev/null +++ b/src/components/TodoList/TodoList.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +export const TodoList = ({ todos, remuve, toggleComplete }) => ( +
    + {todos.map(todo => ( +
  • +
    + { + toggleComplete(todo.id); + }} + type="checkbox" + className="toggle" + id={`todo-${todo.id}`} + /> + +
    + +
  • + ))} +
+); + +TodoList.propTypes = { + todos: PropTypes.arrayOf( + PropTypes.shape({ + content: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + completed: PropTypes.bool.isRequired, + }), + ).isRequired, + remuve: PropTypes.func.isRequired, + toggleComplete: PropTypes.func.isRequired, +}; diff --git a/src/components/TodosFilter/TodosFilter.css b/src/components/TodosFilter/TodosFilter.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/TodosFilter/TodosFilter.js b/src/components/TodosFilter/TodosFilter.js new file mode 100644 index 000000000..a75393d27 --- /dev/null +++ b/src/components/TodosFilter/TodosFilter.js @@ -0,0 +1,36 @@ +import React from 'react'; +import './TodosFilter.css'; +import PropTypes from 'prop-types'; + +export const TodosFilter = ({ updateTodosToShow }) => ( + +); + +TodosFilter.propTypes = { + updateTodosToShow: PropTypes.func.isRequired, +}; diff --git a/src/index.css b/src/index.css index 5e01b4b2e..282a067eb 100644 --- a/src/index.css +++ b/src/index.css @@ -217,7 +217,7 @@ body { transition: color 0.4s; } -.todo-list li.completed label { +.todo-list li .completed label { color: #d9d9d9; text-decoration: line-through; } diff --git a/src/index.js b/src/index.js index ebde5cecc..a7c2f1e16 100644 --- a/src/index.js +++ b/src/index.js @@ -6,5 +6,5 @@ import App from './App'; ReactDOM.render( , - document.getElementById('root') + document.getElementById('root'), );