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
42 changes: 42 additions & 0 deletions src/App.css
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 파일이 왜 다시 생긴 건가용..?-?
현재 과제의 스타일링과 관계없는 vite 기본 스타일링이니 제거해주세요 !

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;
}
72 changes: 72 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useState, useEffect } from 'react';
import Header from './components/Header';
import AddTodo from './components/AddTodo';
import Category from './components/Category';
import TodoList from './components/TodoList';

function App() {
const [todos, setTodos] = useState(() => {
const savedTodos = localStorage.getItem('todos');
return savedTodos ? JSON.parse(savedTodos) : [
{ id: 1, text: 'Eat', completed: false },
{ id: 2, text: 'Sleep', completed: false },
{ id: 3, text: 'Repeat', completed: false },
];
});
const [filter, setFilter] = useState('All');

useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);

const addTodo = (text) => {
const newTodo = {
id: Date.now(),
text,
completed: false,
};
setTodos([newTodo, ...todos]);
};

const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};

const toggleComplete = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};

const editTodo = (id, newText) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, text: newText } : todo
)
);
};

const filteredTodos = todos.filter((todo) => {
if (filter === 'Active') return !todo.completed;
if (filter === 'Completed') return todo.completed;
return true;
});

return (
<div className="App">
<Header />
<AddTodo onAdd={addTodo} />
<Category current={filter} onChange={setFilter} />
<TodoList
todos={filteredTodos}
onDelete={deleteTodo}
onToggle={toggleComplete}
onEdit={editTodo}
/>
</div>
);
}

export default App;
1 change: 1 addition & 0 deletions 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.
24 changes: 24 additions & 0 deletions src/components/AddTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useState } from 'react';

export default function AddTodo({ onAdd }) {
const [input, setInput] = useState('');

const handleAdd = () => {
if (input.trim()) {
onAdd(input);
setInput('');
}
};

return (
<div className="add-todo">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Enter a task"
/>
<button onClick={handleAdd}>Add</button>
</div>
);
}
9 changes: 9 additions & 0 deletions src/components/Category.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Category({ current, onChange }) {
return (
<div className="category">
<button onClick={() => onChange('All')} className={current === 'All' ? 'active' : ''}>All</button>
<button onClick={() => onChange('Active')} className={current === 'Active' ? 'active' : ''}>Active</button>
<button onClick={() => onChange('Completed')} className={current === 'Completed' ? 'active' : ''}>Completed</button>
</div>
);
}
9 changes: 9 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Header() {
return (
<div className="header">
<h1>TodoMatic</h1>
<h2>What needs to be done?</h2>
</div>
);
}

55 changes: 55 additions & 0 deletions src/components/TodoItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState } from 'react';

export default function TodoItem({ todo, onDelete, onToggle, onEdit }) {
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(todo.text);

const handleEdit = () => {
if (isEditing) {
if (editText.trim() !== '') {
onEdit(todo.id, editText);
}
}
setIsEditing(!isEditing);
};

const handleCancel = () => {
setEditText(todo.text);
setIsEditing(false);
};

return (
<div className="todo-item">
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
{isEditing ? (
<>
<input
type="text"
value={editText}
onChange={(e) => setEditText(e.target.value)}
className="edit-input"
/>
<button onClick={handleCancel}>Cancel</button>
<button onClick={handleEdit}>Save</button>
</>
) : (
<>
<span
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
color: todo.completed ? '#aaa' : '#000'
}}
>
{todo.text}
</span>
<button onClick={handleEdit}>Edit</button>
<button className="delete" onClick={() => onDelete(todo.id)}>Delete</button>
</>
)}
</div>
);
}
18 changes: 18 additions & 0 deletions src/components/TodoList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import TodoItem from './TodoItem';

export default function TodoList({ todos, onDelete, onToggle, onEdit }) {
return (
<div className="todo-list">
<p>{todos.length} tasks remaining</p>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
onDelete={onDelete}
onToggle={onToggle}
onEdit={onEdit}
/>
))}
</div>
);
}
98 changes: 98 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
body {
font-family: 'Arial', sans-serif;
background-color: #f7f7f7;
margin: 0;
padding: 2rem;
display: flex;
justify-content: center;
}

.App {
background: white;
padding: 2rem;
width: 400px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
border-radius: 10px;
}

.header {
text-align: center;
margin-bottom: 1.5rem;
}

.add-todo {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
}

.add-todo input {
flex: 1;
padding: 0.5rem;
font-size: 1rem;
}

.add-todo button {
background-color: black;
color: white;
border: none;
padding: 0 1rem;
font-size: 1rem;
cursor: pointer;
}

.category {
display: flex;
justify-content: space-between;
margin-bottom: 1.5rem;
}

.category button {
flex: 1;
padding: 0.5rem;
margin: 0 0.2rem;
border: 1px solid #ccc;
cursor: pointer;
background-color: white;
}

.todo-list p {
font-weight: bold;
margin-bottom: 1rem;
}

.todo-item {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}

.todo-item input[type="checkbox"] {
width: 16px;
height: 16px;
}

.todo-item span {
flex: 1;
}

.todo-item button {
padding: 0.3rem 0.8rem;
border: none;
cursor: pointer;
}

.todo-item button.delete {
background-color: #d9534f;
color: white;
}

.todo-item .edit-input {
flex: 1;
padding: 0.3rem;
margin-right: 0.5rem;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
10 changes: 10 additions & 0 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)