Skip to content
Closed
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
24 changes: 24 additions & 0 deletions To-do List(TS+React)/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
54 changes: 54 additions & 0 deletions To-do List(TS+React)/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default tseslint.config({
extends: [
// Remove ...tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
],
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default tseslint.config({
plugins: {
// Add the react-x and react-dom plugins
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
// other rules...
// Enable its recommended typescript rules
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
})
```
28 changes: 28 additions & 0 deletions To-do List(TS+React)/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
13 changes: 13 additions & 0 deletions To-do List(TS+React)/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions To-do List(TS+React)/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "2nd-mission",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.0",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0"
}
}
1 change: 1 addition & 0 deletions To-do List(TS+React)/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions To-do List(TS+React)/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
18 changes: 18 additions & 0 deletions To-do List(TS+React)/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

import './App.css'
import TodoBefore from './components/TodoBefore'
import Todo from './components/Todo'
import { TodoProvider } from './context/TodoContext';

function App() {

return (
<>
<TodoProvider>
<Todo />
</TodoProvider>
</>
)
}

export default App;
1 change: 1 addition & 0 deletions To-do List(TS+React)/src/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions To-do List(TS+React)/src/components/Todo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { FormEvent, useContext, useState } from 'react';
import {TTodo} from '../types/todo.ts';
import TodoForm from './TodoForm.tsx';
import TodoList from './TodoList.tsx';
import { useTodo } from '../context/TodoContext.tsx';

const Todo = () : React.ReactElement => {


const {todos, completeTodo,addTodo,deleteTodo,doneTodos} = useTodo();





return (
<div className='todo-container'>
<h1 className='todo-container__header'>Yong TODO</h1>
{<TodoForm />}
<div className='render-container'>
<TodoList
title = '할 일'
todos = {todos}
buttonLabel = '완료'
buttonColor = '#28a745'
onClick = {completeTodo}
/>
<TodoList
title = '완료'
todos = {doneTodos}
buttonLabel = '삭제'
buttonColor = '#dc3545'
onClick = {deleteTodo}
/>
</div>
</div>
);
};

export default Todo;
83 changes: 83 additions & 0 deletions To-do List(TS+React)/src/components/TodoBefore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { FormEvent, useState } from 'react';
import {TTodo} from '../types/todo.ts';

const TodoBefore: React.FC = () => {

const [todos, setTodos] = useState<TTodo[]>([]);
const [doneTodos, setDoneTodos] = useState<TTodo[]>([]);
const [input, setInput] = useState<string>('');

console.log('Input',input);

const handleSubmit = (e: FormEvent<HTMLFormElement>) : void => {
e.preventDefault();
console.log('동작함')
const text = input.trim();

if(text) {
const newTodo: TTodo = {id:Date.now(), text};
setTodos((prevTodos) : TTodo[] => [...prevTodos,newTodo]);
setInput('');
}
};

const completeTodo = (todo: TTodo): void => {
setTodos(prevTodos => prevTodos.filter((t) : boolean => t.id !== todo.id));
setDoneTodos(prevDoneTodos => [...prevDoneTodos, todo]);
};

const deleteTodo = (todo: TTodo): void => {
setDoneTodos((prevDoneTodos): TTodo[] =>prevDoneTodos.filter((t): boolean => t.id !== todo.id));
};

return (
<div className='todo-container'>
<h1 className='todo-container__header'>YONG TODO</h1>
<form onSubmit={handleSubmit} className="todo-container__form">
<input
value = {input}
onChange={(e) : void =>setInput(e.target.value)}
type="text"
className="todo-container__input"
placeholder="할 일 입력"
required
/>
<button type="submit" className="todo-container__button">할일 추가</button>
</form>
<div className="render-container">
<div className="render-container__section">
<h2 className="render-container__title">할 일</h2>
<ul id="todo-list" className="render-container__list">
{todos.map((todo) : any => (
<li key={todo.id} className='render-container__item'>
<span className='render-container__item-test'>{todo.text}</span>
<button
onClick={() : void => completeTodo(todo)}
style={{
backgroundColor: '#28a745',
}} className='render-container__item-button'>완료</button>
</li>
))}
</ul>
</div>
<div className="render-container__section">
<h2 className="render-container__title">완료</h2>
<ul id="todo-list" className="render-container__list">
{doneTodos.map((todo) : any => (
<li key={todo.id} className='render-container__item'>
<span className='render-container__item-test'>{todo.text}</span>
<button
onClick={():void => deleteTodo(todo)}
style={{
backgroundColor: '#dc3545',
}} className='render-container__item-button'>삭제</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};

export default TodoBefore;
41 changes: 41 additions & 0 deletions To-do List(TS+React)/src/components/TodoForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { FormEvent, useState } from 'react';
import { useTodo } from '../context/TodoContext';


interface TodoFormProps {
input: string;
setInput: (input: string) => void;
handleSubmit: (e: React.FormEvent<HTMLFormElement>) =>
void;
}

const TodoForm = () : React.ReactElement => {

const [input, setInput] = useState<string>('');
const {addTodo} = useTodo();
const handleSubmit = (e: FormEvent<HTMLFormElement>) : void => {
e.preventDefault();
const text = input.trim();

if(text) {
addTodo(text);
setInput('');
}
};

return(
<form onSubmit={handleSubmit} className="todo-container__form">
<input
value = {input}
onChange={(e) : void =>setInput(e.target.value)}
type="text"
className="todo-container__input"
placeholder="할 일 입력"
required
/>
<button type="submit" className="todo-container__button">할일 추가</button>
</form>
);
};

export default TodoForm;
Loading