Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
# react-todo-list-precourse
# react-todo-list-precourse

## 할 일 목록 애플리케이션

- 이 프로그램은 사용자가 할 일을 입력하여 todo list를 관리할 수 있습니다.

### 사용 방법

1. 입력 필드에 할 일을 입력하고 엔터를 눌러 추가합니다.
2. 체크박스를 클릭하여 할 일을 완료로 표시합니다.
3. "All", "Active", "Completed" 버튼을 클릭하여 할 일을 필터링합니다.
4. "Clear completed" 버튼을 클릭하여 완료된 할 일을 삭제합니다.
5. 입력 필드 왼쪽의 화살표 버튼을 클릭하여 모든 할 일을 완료로 표시합니다.

### 구현된 기능 목록

- 사용자 인터페이스 구성
- 새로운 할 일 추가 기능
- 할 일 완료 표시
- 완료된 할 일 삭제 버튼

### 구현할 기능 목록

- 완료되지 않은 할 일 개수 출력
- 할 일을 상태별로 필터링(All, Active, Completed)
- 모든 할 일 완료로 표시
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<title>todo-list</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="./src/App.js"></script>
</body>
</html>
63 changes: 63 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
body {
background-color: #fce4ec;
}

.inner {
width: 600px;
margin: 0 auto;
}

h1 {
font-size: 70px;
font-family: "Helvetica Neue", "Helvetica", "Arial", "sans-serif";
font-weight: 300;
text-align: center;
}

input#todoInput {
display: block;
width: 400px;
height: 60px;
padding: 0 50px;
margin: 0 auto;
font-size: 25px;
}

ul {
width: 500px;
margin: 20px auto;
padding: 0;
}

li {
display: flex;
align-items: center;
position: relative;
border-bottom: 1px solid black;
list-style: none;
padding: 15px 0px 15px 20px;
}

li:last-child {
border: none;
}

li .todo {
position: absolute;
width: 350px;
margin: auto 70px;
font-size: 25px;
font-weight: 200;
flex-grow: 1;
}

li img {
width: 50px;
height: 50px;
margin-left: auto;
}

.completed .todo {
text-decoration: line-through;
text-decoration-color: #f06292;
}
6 changes: 6 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import Main from './main';

const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(React.createElement(Main));
5 changes: 5 additions & 0 deletions src/components/CheckBox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
input#checkbox {
width: 50px;
height: 50px;
margin: 0;
}
14 changes: 14 additions & 0 deletions src/components/CheckBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "./CheckBox.css";

const CheckBox = ({ isChecked, onChange }) => {
return (
<input
type="checkbox"
id="checkbox"
checked={isChecked}
onChange={onChange}
/>
);
};

export default CheckBox;
18 changes: 18 additions & 0 deletions src/components/DeleteButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import deleteImg from "../img/deleteImg.png";
import deleteTodo from "../utils/deleteTodo";

function DeleteButton({index, todos, setTodos}) {
const handleClick = () => {
deleteTodo(index, todos, setTodos);
};

return(
<img
src={deleteImg}
alt="delete"
onClick={handleClick}
/>
)
}

export default DeleteButton;
14 changes: 14 additions & 0 deletions src/components/Input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function Input({ value, onChange, onKeyPress }) {
return (
<input
type="text"
value={value}
onChange={onChange}
onKeyPress={onKeyPress}
placeholder="What needs to be done? &#128149;"
id="todoInput"
/>
);
}

export default Input;
25 changes: 25 additions & 0 deletions src/components/TodoList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from "react";
import CheckBox from "./CheckBox";
import toggleTodo from "../utils/toggleTodo";
import DeleteButton from "./DeleteButton";
import deleteTodo from "../utils/deleteTodo";

const TodoList = ({ todos, setTodos}) => {
const [completedTodos, setCompletedTodos] = useState([]);

return (
<ul>
{todos.map((todo, index) => (
<li key={index} className={completedTodos.includes(index) ? "completed" : ""}>
<CheckBox
isChecked={completedTodos.includes(index)}
onChange={() => toggleTodo(index, completedTodos, setCompletedTodos)}/>
<div className="todo">{todo}</div>
<DeleteButton index={index} todos={todos} setTodos={setTodos}/>
</li>
))}
</ul>
);
};

export default TodoList;
Binary file added src/img/deleteImg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed src/main.js
Empty file.
27 changes: 27 additions & 0 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useState } from "react";
import "./App.css";
import Input from "./components/Input.jsx";
import TodoList from "./components/TodoList.jsx";
import handleInputChange from "./utils/handleInputChange.jsx";
import handleKeyPress from "./utils/handleKeyPress.jsx";
import addTodo from "./utils/addTodo.jsx";

function Main() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState("");
return (
<div className="inner">
<h1>&#127872; todos</h1>
<Input
value={inputValue}
onChange={(e) => handleInputChange(e, setInputValue)}
onKeyPress={(e) =>
handleKeyPress(e, todos, setTodos, inputValue, setInputValue)
}
/>
<TodoList todos={todos} setTodos={setTodos} />
</div>
);
}

export default Main;
6 changes: 6 additions & 0 deletions src/utils/addTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const addTodo = (todos, inputValue) => {
if (inputValue.trim() === "") return todos;
return [...todos, inputValue];
};

export default addTodo;
5 changes: 5 additions & 0 deletions src/utils/deleteTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const deleteTodo = (index, todos, setTodos) => {
setTodos(todos.filter((_, i) => i !== index));
};

export default deleteTodo;
5 changes: 5 additions & 0 deletions src/utils/handleInputChange.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const handleInputChange = (e, setInputValue) => {
setInputValue(e.target.value);
};

export default handleInputChange;
12 changes: 12 additions & 0 deletions src/utils/handleKeyPress.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const handleKeyPress = (e, todos, setTodos, inputValue, setInputValue) => {
if (e.key === "Enter") {
e.preventDefault();
const newTodo = inputValue.trim(); // 입력된 값의 앞뒤 공백 제거
if (newTodo !== "") { //입력된 값이 공백이 아닌 경우에만
setTodos((prevTodos) => [...prevTodos, newTodo]); // 이전 할 일에 새로운 할 일 추가하여 리스트 업데이트
setInputValue(""); // 입력값 초기화
}
}
};

export default handleKeyPress;
11 changes: 11 additions & 0 deletions src/utils/toggleTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const toggleTodo = (index, completedTodos, setCompletedTodos) => {
//index의 할 일이 이미 완료된 할 일인지 확인
const isCompleted = completedTodos.includes(index);
if (isCompleted) {
setCompletedTodos(completedTodos.filter((item) => item !== index));
} else {
setCompletedTodos([...completedTodos, index]);
}
};

export default toggleTodo;
9 changes: 4 additions & 5 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
plugins: [react({})],
});