■P229

src/components/TaskItem.jsx
────────────────
import { cva } from "class-variance-authority";
import { Trash2 } from "lucide-react";

// 入力フィールドのスタイルを定義
const inputVariants = cva("flex-1 border px-2 py-1 border-gray-300 bg-white", {
  variants: {
    completed: {
      true: "text-gray-400 line-through disabled:cursor-not-allowed", // 完了したタスクのスタイル
    },
  },
});

export function TaskItem({ task }) {
  return (
    <div className="flex items-center gap-3 rounded bg-white px-4 py-2">
      <div className="flex items-center">
        {/* タスクのステータス変更用のチェックボックス */}
        <input
          type="checkbox"
          className="size-5 cursor-pointer"
          checked={task.status === "completed"} // ステータスが completed の場合はチェックON
        />
      </div>
      <input
        type="text"
        className={inputVariants({ completed: task.status === "completed" })}
        defaultValue={task.title}
        disabled={task.status === "completed"}
      />
      {/* タスクをゴミ箱に移動するボタン */}
      <button
        type="button"
        className="rounded bg-gray-200 p-2 transition-colors hover:bg-gray-300"
        aria-label={`タスク「${task.title}」をゴミ箱へ移動する`}
      >
        <Trash2 className="size-5 text-gray-500" />
      </button>
    </div>
  );
};
────────────────


■P231

src/components/TaskItem.jsx
────────────────
import { cva } from "class-variance-authority";
import { Trash2 } from "lucide-react";

// 入力フィールドのスタイルを定義
const inputVariants = cva("flex-1 border px-2 py-1 border-gray-300 bg-white", {
  variants: {
    completed: {
      true: "text-gray-400 line-through disabled:cursor-not-allowed", // 完了したタスクのスタイル
    },
  },
});

export function TaskItem({ task, onChange }) {
  return (
    <div className="flex items-center gap-3 rounded bg-white px-4 py-2">
      <div className="flex items-center">
        <input
          type="checkbox"
          className="size-5 cursor-pointer"
          checked={task.status === "completed"}
          onChange={(e) =>
            onChange(task.id, {
              // チェック状態に基づいてタスクのステータスを更新
              status: e.target.checked ? "completed" : "notStarted",
            })
          }
        />
      </div>
      <input
        type="text"
        className={inputVariants({ completed: task.status === "completed" })}
        defaultValue={task.title}
        disabled={task.status === "completed"}
        onKeyDown={(event) => {
          // Enterキーで編集を確定し、入力フィールドからフォーカスを外す
          if (event.nativeEvent.isComposing || event.key !== "Enter") {
            return;
          }
          event.currentTarget.blur();
        }}
        onBlur={(e) => {
          onChange(task.id, {
            // フォーカスが外れたタイミングでタスクのタイトルを更新
            title: e.target.value,
          });
        }}
      />
      <button
        type="button"
        className="rounded bg-gray-200 p-2 transition-colors hover:bg-gray-300"
        onClick={() =>
          onChange(task.id, {
            status: "trashed", // ステータスをゴミ箱に変更
          })
        }
        aria-label={`タスク「${task.title}」をゴミ箱へ移動する`}
      >
        <Trash2 className="size-5 text-gray-500" />
      </button>
    </div>
  );
};
────────────────


■P233

src/components/TaskList.jsx
────────────────
import { useState } from "react";
import { CreateTaskForm } from "./CreateTaskForm";
import { TaskItem } from "./TaskItem";

export function TaskList() {
  // タスク一覧の状態を管理
  const [taskList, setTaskList] = useState([]);

  // 新しいタスクを追加
  const createTask = (title) => {
    setTaskList((prevTaskList) => {
      // 新しいタスクオブジェクトを生成
      const newTask = {
        id: Date.now(), // 現在の時刻を ID として使用
        title,
        status: "notStarted", // 初期状態は「未着手」
      };
      // 既存のタスク一覧に新しいタスクを追加
      return [...prevTaskList, newTask];
    });
  };

  // ゴミ箱のタスクを除いたタスク一覧
  const activeTaskList = taskList.filter(({ status }) => status !== "trashed");

  // タスクを更新する
  const updateTask = (id, updatedTask) => {
    setTaskList((prevTaskList) => {
      return prevTaskList.map((task) =>
        // 対象タスクのidが一致する場合、そのタスクを更新
        task.id === id ? { ...task, ...updatedTask } : task,
      );
    });
  };

  return (
    <div className="relative">
      <div className="sticky top-0 flex flex-col items-end gap-2 bg-slate-100 px-10 py-5">
        <div className="w-full">
          <CreateTaskForm onSubmit={createTask} />
        </div>
      </div>
      <div className="space-y-3 px-10 pb-10">
        {activeTaskList.length === 0 ? (
          <p className="text-center text-sm">タスクがありません</p>
        ) : (
          activeTaskList.map((task) => (
            <TaskItem key={task.id} task={task} onChange={updateTask} /> // onChangeでupdateTaskを実行
          ))
        )}
      </div>
    </div>
  );
};
────────────────

