From 212502c4b4e7f0701be1db6e860dae12cf2f56bc Mon Sep 17 00:00:00 2001 From: kirikirisu Date: Tue, 4 Feb 2025 14:35:15 +0900 Subject: [PATCH 1/4] Step1: add text input & add todo button --- frontend_training/src/App.css | 41 +++++++++++++++++++++++++++++++++++ frontend_training/src/App.tsx | 4 ++++ 2 files changed, 45 insertions(+) diff --git a/frontend_training/src/App.css b/frontend_training/src/App.css index cce452c..30879aa 100644 --- a/frontend_training/src/App.css +++ b/frontend_training/src/App.css @@ -6,3 +6,44 @@ flex-direction: column; align-items: center; } + +.inputForm { + width: 600px; + display: flex; + align-items: center; + gap: 8px; + height: 38px; +} + +.input { + width: 100%; + height: 100%; + padding: 0 14px; + color: #000; + border: 1px solid rgb(0 0 0 / 20%); + border-radius: 4px; +} + +.addButton { + border-radius: 4px; + width: 60px; + height: 100%; + font-size: 13px; + font-weight: 600; + line-height: 18px; + color: #fff; + background-color: #000; + border: 1px solid rgb(0 0 0 / 20%); + border-radius: 4px; + cursor: pointer; + + /* ホバー時のスタイル */ + &:hover { + background-color: rgb(0 0 0 / 70%); + } + + /* ボタン押下時のスタイル */ + &:active { + background-color: rgb(0 0 0 / 40%); + } +} diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 200cfb8..1523130 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -5,6 +5,10 @@ function App() { return ( <>

TODOアプリ

+
+ + +
) } From 6339bd055b11b04c8e819a3db39edb1ff128cfde Mon Sep 17 00:00:00 2001 From: kirikirisu Date: Tue, 4 Feb 2025 15:07:05 +0900 Subject: [PATCH 2/4] Step2: create todo --- frontend_training/src/App.tsx | 40 ++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 1523130..38a7e6b 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -1,16 +1,46 @@ -import './App.css' +import { useState } from "react"; +import "./App.css"; + +type TodoItem = { + text: string; +}; function App() { + const [inputValue, setInputValue] = useState(""); + const [todoList, setTodoList] = useState([]); return ( <>

TODOアプリ

- - + { + setInputValue(event.target.value); + }} + /> +
+
    + {todoList.map((todo) => { + return
  • {todo.text}
  • ; + })} +
- ) + ); } -export default App +export default App; From 87b3c4644944228226f1b17177dc791c27e04f5a Mon Sep 17 00:00:00 2001 From: kirikirisu Date: Tue, 4 Feb 2025 16:38:54 +0900 Subject: [PATCH 3/4] Step3: complete todo --- frontend_training/package.json | 3 ++- frontend_training/pnpm-lock.yaml | 9 +++++++++ frontend_training/src/App.css | 34 ++++++++++++++++++++++++++++++++ frontend_training/src/App.tsx | 32 +++++++++++++++++++++++++----- 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/frontend_training/package.json b/frontend_training/package.json index 780124b..2ac757f 100644 --- a/frontend_training/package.json +++ b/frontend_training/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "uuid": "^11.0.5" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/frontend_training/pnpm-lock.yaml b/frontend_training/pnpm-lock.yaml index 8295f94..53d495c 100644 --- a/frontend_training/pnpm-lock.yaml +++ b/frontend_training/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + uuid: + specifier: ^11.0.5 + version: 11.0.5 devDependencies: '@eslint/js': specifier: ^9.17.0 @@ -916,6 +919,10 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uuid@11.0.5: + resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} + hasBin: true + vite@6.0.11: resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -1748,6 +1755,8 @@ snapshots: dependencies: punycode: 2.3.1 + uuid@11.0.5: {} + vite@6.0.11: dependencies: esbuild: 0.24.2 diff --git a/frontend_training/src/App.css b/frontend_training/src/App.css index 30879aa..1fbee8c 100644 --- a/frontend_training/src/App.css +++ b/frontend_training/src/App.css @@ -24,6 +24,18 @@ border-radius: 4px; } +.todoList { + display: flex; + flex-direction: column; + gap: 20px; +} + +.todoItem { + display: flex; + gap: 30px; + align-items: center; +} + .addButton { border-radius: 4px; width: 60px; @@ -47,3 +59,25 @@ background-color: rgb(0 0 0 / 40%); } } + +.completeButton { + border-radius: 4px; + width: 60px; + height: 100%; + font-size: 13px; + font-weight: 600; + color: #fff; + background-color: #008000; + border: 1px solid rgb(0 128 0 / 20%); + cursor: pointer; + + /* ホバー時のスタイル */ + &:hover { + background-color: rgb(0 128 0 / 70%); + } + + /* ボタン押下時のスタイル */ + &:active { + background-color: rgb(0 128 0 / 40%); + } +} diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 38a7e6b..2965349 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -1,14 +1,19 @@ import { useState } from "react"; +import { v4 as uuidv4 } from "uuid"; import "./App.css"; type TodoItem = { + id: string; text: string; + isCompleted: boolean; }; function App() { const [inputValue, setInputValue] = useState(""); const [todoList, setTodoList] = useState([]); + const filteredTodoList = todoList.filter((todo) => !todo.isCompleted) + return ( <>

TODOアプリ

@@ -25,18 +30,35 @@ function App() { className="addButton" type="button" onClick={() => { - if (inputValue === "") return + if (inputValue === "") return; - setTodoList((prev) => [...prev, { text: inputValue }]); + setTodoList((prev) => [ + ...prev, + { id: uuidv4(), text: inputValue, isCompleted: false }, + ]); setInputValue(""); }} > 追加 -
    - {todoList.map((todo) => { - return
  • {todo.text}
  • ; +
      + {filteredTodoList.map((todo) => { + return ( +
      +
    • {todo.text}
    • + +
      + ); })}
    From ba0b59f6b9cbd68a5ce249b29493a2857ce4b607 Mon Sep 17 00:00:00 2001 From: kirikirisu Date: Tue, 4 Feb 2025 17:36:39 +0900 Subject: [PATCH 4/4] Step4: update todo --- frontend_training/src/App.css | 66 +++++++++++++++++++- frontend_training/src/App.tsx | 109 ++++++++++++++++++++++++++++++---- 2 files changed, 160 insertions(+), 15 deletions(-) diff --git a/frontend_training/src/App.css b/frontend_training/src/App.css index 1fbee8c..c89219f 100644 --- a/frontend_training/src/App.css +++ b/frontend_training/src/App.css @@ -67,17 +67,77 @@ font-size: 13px; font-weight: 600; color: #fff; - background-color: #008000; + background-color: #008000; border: 1px solid rgb(0 128 0 / 20%); cursor: pointer; - + /* ホバー時のスタイル */ &:hover { background-color: rgb(0 128 0 / 70%); } - + /* ボタン押下時のスタイル */ &:active { background-color: rgb(0 128 0 / 40%); } } + +.editItem { + display: flex; + gap: 30px; + align-items: center; + height: 25px; +} + +.actionButtons { + width: 100%; + height: 100%; + display: flex; + gap: 5px; +} + +.updateButton { + border-radius: 4px; + width: fit-content; + height: 100%; + font-size: 13px; + font-weight: 600; + line-height: 18px; + color: #fff; + background-color: #007bff; + border: 1px solid rgba(0, 123, 255, 0.2); + cursor: pointer; + + /* ホバー時のスタイル */ + &:hover { + background-color: rgba(0, 123, 255, 0.7); + } + + /* ボタン押下時のスタイル */ + &:active { + background-color: rgba(0, 123, 255, 0.4); + } +} + +.cancelButton { + border-radius: 4px; + width: fit-content; + height: 100%; + font-size: 13px; + font-weight: 600; + line-height: 18px; + color: #fff; + background-color: #dc3545; + border: 1px solid rgba(220, 53, 69, 0.2); + cursor: pointer; + + /* ホバー時のスタイル */ + &:hover { + background-color: rgba(220, 53, 69, 0.7); + } + + /* ボタン押下時のスタイル */ + &:active { + background-color: rgba(220, 53, 69, 0.4); + } +} diff --git a/frontend_training/src/App.tsx b/frontend_training/src/App.tsx index 2965349..b725a6d 100644 --- a/frontend_training/src/App.tsx +++ b/frontend_training/src/App.tsx @@ -6,13 +6,14 @@ type TodoItem = { id: string; text: string; isCompleted: boolean; + isEdit: boolean; }; function App() { const [inputValue, setInputValue] = useState(""); const [todoList, setTodoList] = useState([]); - const filteredTodoList = todoList.filter((todo) => !todo.isCompleted) + const filteredTodoList = todoList.filter((todo) => !todo.isCompleted); return ( <> @@ -34,7 +35,12 @@ function App() { setTodoList((prev) => [ ...prev, - { id: uuidv4(), text: inputValue, isCompleted: false }, + { + id: uuidv4(), + text: inputValue, + isCompleted: false, + isEdit: false, + }, ]); setInputValue(""); }} @@ -44,19 +50,44 @@ function App() {
      {filteredTodoList.map((todo) => { + if (todo.isEdit) { + return ; + } + return (
      -
    • {todo.text}
    • - + }} + > + 完了 +
      ); })} @@ -66,3 +97,57 @@ function App() { } export default App; + +function EditTodo({ + todo, + setTodoList, +}: { + todo: TodoItem; + setTodoList: React.Dispatch>; +}) { + const [inputValue, setInputValue] = useState(todo.text); + + return ( +
      + { + setInputValue(event.target.value); + }} + className="input" + /> +
      + + +
      +
      + ); +}