Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FE 01 입니다 #9

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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
35 changes: 15 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
# 42gg 프론트엔드 온보딩 1단계
# 📝 ToDo List 만들기

## 공통 조건
## ✨ 주요 기능 소개
### ✏️ 할 일을 추가할 수 있습니다
- 오늘의 할일을 입력하고, 엔터키를 통해서 제출해서 새로운 할 일을 등록합니다.
<img width="400" height="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/f944c863-1f51-494f-952b-e056f64ffd66">
<img width="400" hegiht="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/0bd9c197-f105-4878-b7b9-04e2e5803b5f">

- 온보딩 프로젝트는 개인 계정으로 fork하여 진행하고 PR로 제출합니다.
- git / github / code 컨벤션은 42gg notion에 있는 자료를 적극 반영합니다.
- 기본 기능 외 추가 기능, 디자인 구현은 자유입니다.
- 최종 제출품에는 README 작성이 되있어야 합니다.([예시](https://github.com/42organization/42gg.client/blob/main/README.md))

## todo list 만들기
### ✏️ 할 일을 삭제할 수 있습니다
- 삭제 버튼을 이용해서 삭제가능합니다.
<img width="400" hegiht="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/0bd9c197-f105-4878-b7b9-04e2e5803b5f">
<img width="400" height="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/f944c863-1f51-494f-952b-e056f64ffd66">

- (필수) Javascript, HTML, CSS
- (필수) todo 생성(Create), 조회(Read) 기능 구현하기 (새로고침 고려 X)
- (필수) todo 완료 상태 체크 기능 구현하기 (정렬은 선택사항)
- (선택) todo 수정(Update), 삭제(Delete) 기능 구현하기 (새로고침 고려 X)
- (선택) 디자인 적용하기
- (선택) 무료로 배포하기
### ✏️ 할 일을 수정할 수 있습니다
- 수정버튼을 클릭하고, 내용을 수정 후 다시 수정버튼을 클릭하면 기존의 할일을 수정 가능합니다.
<img width="400" height="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/5db5f11a-50ef-4760-952b-f02929e41642">

## 참고
### ✏️ 완료한 할 일은 클릭하여 완료표시할 수 있습니다

- 데이터 관리는 하단의 방식 중 하나 선택하시면 됩니다.
- localstorage
- local server(예. [https://github.com/shal0mdave/todo-api.git](https://github.com/shal0mdave/todo-api.git), lowdb)
- mock api(예. [https://dummyjson.com/](https://dummyjson.com/))
- todo list를 구현하기 위해 필요한 기능들을 미리 생각(그려보고)해보고, 구현해보세요.
- 궁금한 사항은 issue에 등록해주세요.
<img width="400" height="500" alt="image" src="https://github.com/joonho0410/42gg-onboarding-fe-01/assets/76806109/cc632a40-10b5-4b4d-beb4-3e41d9198fbb">
20 changes: 20 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang=""en>
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="styles.css">
<title>Todo List</title>
</head>
<body>
<div class="container">
<h1>To-Do List</h1>
<form id="todoForm">
<input type="text" placeholder="오늘의 할일"/>
</form>
<ul id="todos"></ul>
</div>
<script src="todo.js"></script>
</body>
</html>
93 changes: 93 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
body {
font-family: Arial, sans-serif;
}

.container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #3498db;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
color: #3498db;
}

form {
display: flex;
margin-bottom: 10px;
}

form input[type="text"] {
flex: 1;
padding: 10px;
font-size: 16px;
}

ul {
list-style-type: none;
padding: 0;
max-width: 100%;
max-height: 800px;
overflow-y: auto;
border: 1px solid #ccc;
}

ul li.todo-item {
max-width: 100%;
padding: 10px;
border-bottom: 1px solid #ccc;
position:relative;
overflow-y: auto;
display: flex;
align-items:center;
}

ul li.todo-text {
width: 80%;
flex: 1;
white-space: pre-wrap;
word-wrap: break-word;
}

ul li.todo-item.completed {
background-color: #f0f0f0;
color: red;
}

ul li.todo-item.completed span.todo-text {
text-decoration: line-through;
}

ul li:last-child {
border-bottom: none;
}

ul li span {
font-size: 18px;
flex: 1;
}

.edit-input {
display: none;
font-size: 18px;
padding: 5px;
width: 80%;
flex: 1;
}

button {
background-color: white;
font-weight: bold;
border: none;
color: skyblue;
}

ul li.todo-item.completed button{
background-color: #f0f0f0;
font-weight: bold;
border: none;
color: skyblue;
}
137 changes: 137 additions & 0 deletions todo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const todoForm = document.getElementById("todoForm");
const todoInput = todoForm.querySelector('input');
const todos = document.getElementById("todos");

const TODOLIST = "todoList";
let todoList = [];

function init() {
loadTodoList();
todoForm.addEventListener("submit", createTodo);
}

function makeDone(event){
event.preventDefault();
if (event.target.tagName === 'LI'){
const parent = event.target;
const index = todoList.findIndex((todo) => todo.id === String(parent.id));

todoList[index].isDone = !todoList[index].isDone;
if (todoList[index].isDone)
parent.classList.add("completed");
else
parent.classList.remove("completed");

localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

if (event.target.tagName === "SPAN" ) {
const parent = event.target.parentNode;
const index = todoList.findIndex((todo) => todo.id === String(parent.id));

todoList[index].isDone = !todoList[index].isDone;
if (todoList[index].isDone)
parent.classList.add("completed");
else
parent.classList.remove("completed");

localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}
}

function saveTodo(todo, isDone ,id) {
const todoObj = {
text: todo,
id: id,
isDone: isDone,
}
todoList.push(todoObj);
localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

function loadTodoList(){
const loadedTodoList = localStorage.getItem(TODOLIST);
if (loadedTodoList !== null){
const parseTodoList = JSON.parse(loadedTodoList);
for(let todo of parseTodoList) {
const {text} = todo;// const text = todo.text;
const {isDone} = todo;
const {id} = todo;
paintTodo(text, isDone, id);
saveTodo(text, isDone, id);
}
}
}
init();

function createTodo(event){
event.preventDefault();
const todo = todoInput.value;
const id = Math.random().toString(36).substr(2, 9);
paintTodo(todo, false, id);
saveTodo(todo, false, id);
todoInput.value = "";
}

function delTodo(event){
const { target:button } = event;
const li = button.parentNode;
todos.removeChild(li);
todoList = todoList.filter((todo) => todo.id !== String(li.id));
localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

function uptTodo(event){
const parent = event.target.parentNode;
const span = parent.querySelector('span');
const input = parent.querySelector('input');

if (input.style.display === 'none' || input.style.display === '') {
span.style.display = 'none';
input.style.display = 'inline-block';
input.value = span.textContent;
} else {
span.textContent = input.value;
span.style.display = 'inline-block';
input.style.display = 'none';

const parent = event.target.parentNode;
const index = todoList.findIndex((todo) => todo.id === String(parent.id));

todoList[index].text = input.value;
localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}
}

function paintTodo(todo, isDone, id) {
const li = document.createElement("li")
const span = document.createElement("span");
const delButton = document.createElement("button");
const uptButton = document.createElement("button");
const uptInput = document.createElement("input");

li.addEventListener("click", makeDone);
delButton.addEventListener("click", delTodo);
uptButton.addEventListener("click", uptTodo);
span.innerText = todo;
delButton.innerText = "삭제";
uptButton.innerText = "수정";

li.id = id;
li.classList.add("todo-item");
span.classList.add("todo-text");
uptInput.classList.add("edit-input")

li.appendChild(span);
li.appendChild(uptInput);
li.appendChild(delButton);
li.appendChild(uptButton);
// li.id = todoList.length + 1;

if (isDone) {
li.classList.add("completed");
span.classList.add("completed");
}

todos.appendChild(li);
}