diff --git a/README.md b/README.md index 3c0710d2..79eb33b8 100644 --- a/README.md +++ b/README.md @@ -1 +1,10 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +## 기능 구현 목록 +할 일을 입력 받는다. +할 일을 엔터키 또는 버튼을 사용하여 추가할 수 있다. +할 일을 버튼을 사용하여 삭제할 수 있다. +사용자가 아무것도 입력하지 않은 경우에는 할 일을 추가할 수 없다. +할 일의 목록을 볼 수 있다. +할 일의 완료 상태를 전환할 수 있다. +새로고침을 하여도 이전에 작성한 데이터는 유지되어야 한다. \ No newline at end of file diff --git a/index.html b/index.html index b021b5c8..c0746e51 100644 --- a/index.html +++ b/index.html @@ -3,10 +3,11 @@ - + + todo list
- + diff --git a/package-lock.json b/package-lock.json index 636050b2..6190fb2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", "typescript": "^5.2.2", - "vite": "^5.2.0" + "vite": "^5.2.13" }, "engines": { "node": ">=18.18.0", @@ -3146,9 +3146,9 @@ } }, "node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", + "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", "dev": true, "dependencies": { "esbuild": "^0.20.1", diff --git a/package.json b/package.json index d9003d58..af709fee 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", "typescript": "^5.2.2", - "vite": "^5.2.0" + "vite": "^5.2.13" }, "engines": { "npm": ">=9.8.1", diff --git a/src/assets/App.css b/src/assets/App.css new file mode 100644 index 00000000..35920e00 --- /dev/null +++ b/src/assets/App.css @@ -0,0 +1,20 @@ +body { + background-color: rgb(253, 247, 227); + } + +.title{ + margin-top: 80px; + font-size: 80px; + color: rgb(238, 167, 167); + font-weight: 200; +} + +.board { + display: flex; + flex-direction: column; + align-items: center; + +} + + + diff --git a/src/assets/App.jsx b/src/assets/App.jsx new file mode 100644 index 00000000..bfd50c99 --- /dev/null +++ b/src/assets/App.jsx @@ -0,0 +1,39 @@ +import { useState } from 'react' +import './App.css' +import ListContainer from '../components/ListContainer' +import InputForm from '../components/InputForm' + +function App() { + const [Todo, setTodo] = useState([]); + + //할 일 상태 전환 함수 + const complete = (index) => { + const todoList = [...Todo]; + todoList[index].completed = !todoList[index].completed; + setTodo(todoList); + }; + + //할 일 삭제 함수 + const remove = (index) => { + const todoList = [...Todo]; + todoList.splice(index, 1); + setTodo(todoList); + }; + + return ( +
+
todos
+ + +
+ ) +} + +export default App diff --git a/src/assets/main.jsx b/src/assets/main.jsx new file mode 100644 index 00000000..8460c449 --- /dev/null +++ b/src/assets/main.jsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' + +ReactDOM.createRoot(document.getElementById('app')).render( + + + , +) \ No newline at end of file diff --git a/src/components/InputForm.jsx b/src/components/InputForm.jsx new file mode 100644 index 00000000..3d24cbc2 --- /dev/null +++ b/src/components/InputForm.jsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react' +import '../styles/InputForm.css' + +export default function InputForm({ Todo, setTodo }) { + + const [value, setValue] = useState(''); + + //할 일을 State에 추가 + const add = (text) => { + if(text === '') + return; + + setTodo([...Todo, {value: text, completed: false}]); + setValue(''); + } + + //add 호출 + const handleSubmit = (e) => { + e.preventDefault(); + add(value); + } + + return ( +
+ setValue(e.target.value)} + /> + +
+ ); +} diff --git a/src/components/List.jsx b/src/components/List.jsx new file mode 100644 index 00000000..1464ad21 --- /dev/null +++ b/src/components/List.jsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react' +import '../styles/List.css' + +export default function List({ todo, index, remove, complete }) { + + const [isClicked, setIsClicked] = useState(false); + + //체크박스 클릭시 상태전환 함수 호출 + const handleBoxClick = () => { + complete(index); + setIsClicked(!isClicked); + }; + + return ( +
+
+ +
{todo.value}
+ +
+
+ ) +} diff --git a/src/components/ListContainer.jsx b/src/components/ListContainer.jsx new file mode 100644 index 00000000..4545e6ff --- /dev/null +++ b/src/components/ListContainer.jsx @@ -0,0 +1,20 @@ +import React from 'react' +import InputForm from './InputForm' +import List from './List' + +export default function ListContainer({ Todo, remove, complete }) { + return ( + <> +
+ {Todo.map((todo, index) => ( + + ))} +
+ + ) +} diff --git a/src/main.js b/src/main.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/styles/InputForm.css b/src/styles/InputForm.css new file mode 100644 index 00000000..44558e86 --- /dev/null +++ b/src/styles/InputForm.css @@ -0,0 +1,96 @@ +/* inputForm */ +.inputForm { + display: flex; + justify-content: space-around; + align-items: center; + border: 1px solid #e0f1ffbb; + width: 500px; + height: 65px; + padding: 0 20px; + background-color: #fff; + } + + /* inputTodo */ + .inputTodo { + border: none; + font-size: 20px; + } + .inputTodo::placeholder{ + color: rgba(182, 189, 189, 0.836) + } + .inputTodo:focus { + outline: none; + } + + + /* submit-btn */ + .button2 { + display: inline-block; + transition: all 0.2s ease-in; + position: relative; + overflow: hidden; + z-index: 1; + color: #090909; + padding: 0.7em 1.7em; + cursor: pointer; + font-size: 18px; + border-radius: 0.5em; + background: #e8e8e8; + border: 1px solid #e8e8e8; + box-shadow: 6px 6px 12px #c5c5c5, -6px -6px 12px #ffffff; + width: 130px; + height: 40px; + } + + .button2:active { + color: #666; + box-shadow: inset 4px 4px 12px #c5c5c5, inset -4px -4px 12px #ffffff; + } + + .button2:before { + content: ""; + position: absolute; + left: 50%; + transform: translateX(-50%) scaleY(1) scaleX(1.25); + top: 100%; + width: 140%; + height: 180%; + background-color: rgba(0, 0, 0, 0.05); + border-radius: 50%; + display: block; + transition: all 0.5s 0.1s cubic-bezier(0.55, 0, 0.1, 1); + z-index: -1; + } + + .button2:after { + content: ""; + position: absolute; + left: 55%; + transform: translateX(-50%) scaleY(1) scaleX(1.45); + top: 180%; + width: 140%; + height: 190%; + background-color: #009087; + border-radius: 50%; + display: block; + transition: all 0.5s 0.1s cubic-bezier(0.55, 0, 0.1, 1); + z-index: -1; + } + + .button2:hover { + color: #ffffff; + border: 1px solid #009087; + } + + .button2:hover:before { + top: -35%; + background-color: #009087; + transform: translateX(-50%) scaleY(1.3) scaleX(0.8); + } + + .button2:hover:after { + top: -45%; + background-color: #009087; + transform: translateX(-50%) scaleY(1.3) scaleX(0.8); + } + \ No newline at end of file diff --git a/src/styles/List.css b/src/styles/List.css new file mode 100644 index 00000000..b28b94bc --- /dev/null +++ b/src/styles/List.css @@ -0,0 +1,80 @@ +/* list-container */ +.list-container { + display: flex; + justify-content: space-between; + align-items: center; + border: 1px solid #e0f1ffbb; + width: 500px; + height: 65px; + padding: 0 20px; + font-size: 17px; + color: #1d1f1f96; + background-color: #fff; + } + + + /* Hide the default checkbox */ + .container input { + display: none; + } + + .container { + display: block; + position: relative; + cursor: pointer; + font-size: 20px; + user-select: none; + -webkit-tap-highlight-color: transparent; + } + + /* Create a custom checkbox */ + .checkmark { + position: relative; + top: 0; + left: 0; + height: 1.3em; + width: 1.3em; + background-color: #2196F300; + border-radius: 0.25em; + transition: all 0.25s; + } + + /* When the checkbox is checked, add a blue background */ + .container input:checked ~ .checkmark { + background-color: #2196F3; + } + + /* Create the checkmark/indicator (hidden when not checked) */ + .checkmark:after { + content: ""; + position: absolute; + transform: rotate(0deg); + border: 0.1em solid black; + left: 0; + top: 0; + width: 1.05em; + height: 1.05em; + border-radius: 0.25em; + transition: all 0.25s, border-width 0.1s; + } + + /* Show the checkmark when checked */ + .container input:checked ~ .checkmark:after { + left: 0.45em; + top: 0.25em; + width: 0.25em; + height: 0.5em; + border-color: #fff0 white white #fff0; + border-width: 0 0.15em 0.15em 0; + border-radius: 0em; + transform: rotate(45deg); + } + + /* removeBtn */ + .removeBtn { + border: none; + background-color: white; + font-size: large; + color: rgb(243, 96, 96); + } + \ No newline at end of file