Compare commits

3 Commits

View File

@@ -1,103 +1,115 @@
import Image from "next/image"; "use client";
import { useState } from "react";
// Todo型の定義
type Todo = {
id: number;
text: string;
completed: boolean;
};
export default function Home() { export default function Home() {
return ( // 状態管理の準備(サンプルデータ付き)
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> const [todos, setTodos] = useState<Todo[]>([
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"> { id: 1, text: "TypeScriptを学ぶ", completed: false },
<Image { id: 2, text: "Reactの基礎を理解する", completed: true },
className="dark:invert" { id: 3, text: "TODOアプリを作る", completed: false },
src="/next.svg" ]);
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
src/app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row"> // 入力中のテキストを管理
<a const [inputText, setInputText] = useState("");
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" // TODO追加機能
target="_blank" const handleAddTodo = (e: React.FormEvent) => {
rel="noopener noreferrer" e.preventDefault(); // フォームのデフォルト動作を防ぐ
>
<Image if (inputText.trim() === "") {
className="dark:invert" return; // 空文字の場合は追加しない
src="/vercel.svg" }
alt="Vercel logomark"
width={20} const newTodo: Todo = {
height={20} id: Date.now(), // 簡易的なID生成
text: inputText,
completed: false,
};
setTodos([...todos, newTodo]); // 新しいTODOを配列に追加
setInputText(""); // 入力欄をクリア
};
// TODO完了状態の切り替え機能
const handleToggleTodo = (id: number) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
// TODO削除機能
const handleDeleteTodo = (id: number) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<main className="min-h-screen p-8 bg-gray-50 dark:bg-gray-900">
<div className="max-w-4xl mx-auto">
<h1 className="text-4xl font-bold text-center text-gray-800 dark:text-gray-100 mb-8">
TODOアプリ
</h1>
{/* TODO入力フォーム */}
<div className="mb-8">
<form onSubmit={handleAddTodo} className="flex gap-2">
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="新しいTODOを入力..."
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100"
/> />
Deploy now <button
</a> type="submit"
<a className="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]" >
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank" </button>
rel="noopener noreferrer" </form>
>
Read our docs
</a>
</div> </div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center"> {/* TODOリスト表示エリア */}
<a <div className="space-y-2">
className="flex items-center gap-2 hover:underline hover:underline-offset-4" {todos.map((todo) => (
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" <div
target="_blank" key={todo.id}
rel="noopener noreferrer" className="flex items-center gap-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm"
> >
<Image <input
aria-hidden type="checkbox"
src="/file.svg" checked={todo.completed}
alt="File icon" onChange={() => handleToggleTodo(todo.id)}
width={16} className="w-5 h-5 text-blue-500 rounded focus:ring-2 focus:ring-blue-500"
height={16} />
/> <span
Learn className={`flex-1 ${
</a> todo.completed
<a ? "line-through text-gray-500 dark:text-gray-400"
className="flex items-center gap-2 hover:underline hover:underline-offset-4" : "text-gray-800 dark:text-gray-100"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" }`}
target="_blank" >
rel="noopener noreferrer" {todo.text}
> </span>
<Image <button
aria-hidden onClick={() => handleDeleteTodo(todo.id)}
src="/window.svg" className="px-3 py-1 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors"
alt="Window icon" >
width={16}
height={16} </button>
/> </div>
Examples ))}
</a> </div>
<a </div>
className="flex items-center gap-2 hover:underline hover:underline-offset-4" </main>
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
); );
} }