Skip to content

[손호민] TS Todo Refactor 과제 #1

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

Open
wants to merge 5 commits into
base: sonhomin
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

## ⏰ 스터디 시간

**매주 월요일 10:00**
~~매주 월요일 10:00~~

**월요일 16:00**

<br/>

Expand All @@ -31,9 +33,8 @@
## ⚙️ 컨벤션

* 본인 이름의 브랜치에 정리 파일 및 실습 파일 올리고 PR 보내기
* 파일명 규칙
* 본인 이름 폴더 / 챕터 {번호 및 제목} / {정리}.md
* 본인 이름 폴더 / 챕터 {번호 및 제목} / Practice / {문제}.ts
* 파일업로드
* `본인 이름 폴더` / 내에 정리 md 파일, 예제문제 풀이 ts파일

<br/>

Expand All @@ -43,5 +44,9 @@
| 회차 | 일시 | 스터디 내용 |
| ---- | -------- | -------- |
| 1 | 23.11.13 월 | CHAPTER 1, 2, 3, 4 |
| 2 | 23.11.20 월 | CHAPTER 5, 6, 7 ,10 |
| 3 | 23.11.27 월 | CHAPTER 8,9 + type-challenges 워밍업(1) & 쉬움(13) |
| 4 | 23.12.04 월 | CHAPTER 15, Vue+TS 과제 중 이슈 공유 |
| 5 | 23.12.11 월 | TodoList 과제 TS 전환 |


16 changes: 16 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>h0ber Ts</title>
</head>
<body>
<main id="app"></main>
<script
type="module"
src="./src/main.js"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "fedc5_learningts_study",
"version": "1.0.0",
"description": "[러닝 타입스크립트](https://www.yes24.com/Product/Goods/116585556)로 타입스크립트 뿌시기 !",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"typescript": "^5.3.3"
}
}
68 changes: 68 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Header from './components/Header.js';
import createTodo from './components/createTodoInput.js';
import TodoList from './components/TodoList.js';
import TodoCounter from './components/TodoCounter.js';
const HEADER_TITLE = 'TODO LIST';
export default class App {
constructor({ $app, initialState }) {
this.$app = $app;
this.state = initialState;
new Header({
$app: this.$app,
title: HEADER_TITLE,
});
new createTodo({
$app: this.$app,
onSubmit: (text) => {
const nextState = makeNextState(text);
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},
});
const todoList = new TodoList({
$app: this.$app,
initialState: this.state,
updateTodoCounter: (nextState) => {
todoCounter.setState(nextState);
},
onRemoveTodo: (id) => {
const nextState = this.state.filter((todo) => todo.id !== id);
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},
onToggleTodo: (id) => {
const nextState = this.state.map((todo) => {
if (todo.id === id) {
return {
...todo,
isCompleted: !todo.isCompleted,
};
}
return todo;
});
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},
});
const todoCounter = new TodoCounter({
$app: this.$app,
initialState: this.state,
});
const makeNextState = (text) => {
console.log(this.state);
const nextState = [
...this.state,
{
isCompleted: false,
title: text,
id: new Date().getTime().toString(),
},
];
this.state = nextState;
return nextState;
};
}
}
82 changes: 82 additions & 0 deletions src/App.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Todo, AppProps } from './util/types.js';

import Header from './components/Header.js';
import createTodo from './components/createTodoInput.js';
import TodoList from './components/TodoList.js';
import TodoCounter from './components/TodoCounter.js';

const HEADER_TITLE = 'TODO LIST';

export default class App {
private readonly $app: HTMLDivElement;
private state: Todo[];

constructor({ $app, initialState }: AppProps) {
this.$app = $app;
this.state = initialState;

new Header({
$app: this.$app,
title: HEADER_TITLE,
});

new createTodo({
$app: this.$app,
onSubmit: (text: string) => {
const nextState = makeNextState(text);
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},
});

const todoList = new TodoList({
$app: this.$app,
initialState: this.state,
updateTodoCounter: (nextState: Todo[]) => {
todoCounter.setState(nextState);
},

onRemoveTodo: (id: string) => {
const nextState = this.state.filter((todo) => todo.id !== id);
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},

onToggleTodo: (id: string) => {
const nextState = this.state.map((todo) => {
if (todo.id === id) {
return {
...todo,
isCompleted: !todo.isCompleted,
};
}
return todo;
});
this.state = nextState;
todoList.setState(nextState);
todoCounter.setState(nextState);
},
});

const todoCounter = new TodoCounter({
$app: this.$app,
initialState: this.state,
});

const makeNextState = (text: string): Todo[] => {
console.log(this.state);
const nextState = [
...this.state,
{
isCompleted: false,
title: text,
id: new Date().getTime().toString(),
},
];
this.state = nextState;
return nextState;
};
}
}
12 changes: 12 additions & 0 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default class Header {
constructor({ $app, title }) {
this.$header = document.createElement('h1');
this.$app = $app;
this.title = title;
this.render();
}
render() {
this.$header.textContent = this.title;
this.$app.appendChild(this.$header);
}
}
19 changes: 19 additions & 0 deletions src/components/Header.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HeaderProps } from '../util/types.js';

export default class Header {
private readonly $app: HTMLDivElement;
private readonly title: string;
private readonly $header: HTMLHeadElement;

constructor({ $app, title }: HeaderProps) {
this.$header = document.createElement('h1');
this.$app = $app;
this.title = title;
this.render();
}

render(): void {
this.$header.textContent = this.title;
this.$app.appendChild(this.$header);
}
}
18 changes: 18 additions & 0 deletions src/components/TodoCounter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export default class TodoCounter {
constructor({ $app, initialState }) {
this.$counter = document.createElement('div');
$app.appendChild(this.$counter);
this.state = initialState;
this.render();
}
setState(nextState) {
this.state = nextState;
this.render();
}
render() {
this.$counter.innerHTML = `
<div class="todo-counter">Total: ${this.state.length}</div>
<div class="done-counter">Done: ${this.state.filter((e) => e.isCompleted).length}</div>
`;
}
}
27 changes: 27 additions & 0 deletions src/components/TodoCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Todo, TodoCounterProps } from '../util/types.js';

export default class TodoCounter {
private state: Todo[];
private readonly $counter: HTMLDivElement;

constructor({ $app, initialState }: TodoCounterProps) {
this.$counter = document.createElement('div');
$app.appendChild(this.$counter);
this.state = initialState;
this.render();
}

setState(nextState: Todo[]): void {
this.state = nextState;
this.render();
}

render(): void {
this.$counter.innerHTML = `
<div class="todo-counter">Total: ${this.state.length}</div>
<div class="done-counter">Done: ${
this.state.filter((e) => e.isCompleted).length
}</div>
`;
}
}
49 changes: 49 additions & 0 deletions src/components/TodoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { setItem } from '../util/storage.js';
const STORAGE_KEY = 'todo';
export default class TodoList {
constructor({ $app, initialState, updateTodoCounter, onRemoveTodo, onToggleTodo, }) {
this.$todoList = document.createElement('div');
$app.appendChild(this.$todoList);
this.onRemoveTodo = onRemoveTodo;
this.onToggleTodo = onToggleTodo;
this.updateTodoCounter = updateTodoCounter;
this.state = initialState;
this.setEvent();
this.render();
}
setState(nextState) {
setItem(STORAGE_KEY, JSON.stringify(nextState));
this.updateTodoCounter(nextState);
this.state = nextState;
this.render();
}
render() {
this.$todoList.innerHTML = `
<ul>
${this.state
.map(({ title, isCompleted, id }) => {
return `
<li class="toggled-text" data-id="${id}"
style="text-decoration: ${isCompleted ? 'line-through' : 'none'}">
${title}
<button class="remove-button" data-id="${id}">
Remove
</button>
</li>
`;
})
.join('')}
</ul>
`;
}
setEvent() {
this.$todoList.addEventListener('click', (event) => {
const target = event.target;
const id = target.dataset.id;
if (id) {
target.className === 'toggled-text' && this.onToggleTodo(id);
target.className === 'remove-button' && this.onRemoveTodo(id);
}
});
}
}
Loading