0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Reactを使ってTODOアプリを作成する Material UIを用いたデザイン化

Last updated at Posted at 2023-05-13

TODOアプリ

前回に引き続きReactを用いてTODOアプリを作成していこうと思います。
ただし、React初心者なので以下の手順で作成していこうと思います。

  1. JavaScriptで作成(サーバー側はJSON Serverを使用しaxiosを用いてデータの取得、更新を行う)
  2. JavaScript → TypeScriptに変換
  3. Material UIを使用してデザインをつける
  4. Java(Spring Boot)を用いてサーバー側を構築
  5. AWS?を使用してデプロイする

今回はMaterial UIを用いたTODOアプリのデザイン化を行っていきます。

前回まで

前回まではデザイン化を行う前の事前準備を行いました。

成果物

Netlifty

GitHub

ツリー構成

src
 ┣apis                                            ← モックサーバーと通信するファイルを格納するディレクトリ
 ┃  ┗todos.js                                     ← サーバーとの通信用のファイル(CRUD)
 ┣components                                      ← コンポーネントを格納するディレクトリ
 ┃    ┣App.js                                     ← コンポーネントをまとめるファイル
 ┃    ┣TodoAdd.js                                 ← TODOを新規追加するコンポーネント
 ┃    ┣TodoAddCheckItem.js                        ← チェックリスト単体用のコンポーネント
 ┃    ┣TodoAddCheckList.js                        ← チェックリストをまとめるコンポーネント
 ┃    ┣TodoItem.js                                ← TODO単体用のコンポーネント
 ┃    ┣TodoList.js                                ← TODOをリスト化するコンポーネント
 ┃    ┗TodoTitle.js                               ← タイトル用のコンポーネント
  ┣css                                   ← CSSファイルをまとめるディレクトリ
 ┃  ┣AppStyle.module.css                 ← Appコンポーネントに適用するCSS Module
 ┃  ┣common.css                                                    ← ファイル全体に適用させる共通CSS
 ┃  ┣reset.css                           ← ファイル全体に適用させるリセットCSS
 ┃  ┣TodoAddCheckItemSytle.module.css    ← TodoAddCheckItemコンポーネントに適用するCSS Module
 ┃  ┣TodoAddModalStyle.module.css        ← TodoAddModalコンポーネントに適用するCSS Module
 ┃  ┣TodoItemStyle.module.css            ← TodoItemStyleコンポーネントに適用するCSS Module
 ┃  ┗TodoTitleStyle.module.css           ← TodoTitleStyleコンポーネントに適用するCSS Module
 ┣hooks                                 ← カウタムフックを格納するディレクトリ
 ┃  ┗useTodo.js                         ← TODOの状態を管理するカスタムフック(todos.jsの具体的な実装部分)
 ┣index.js                              ← TODOアプリのトップルート
  ┗types.ts                                                            ← TypeScriptで使用する型定義がまとめられたファイル
// 省略
db.json                                                                  ← モックサーバー
global.d.ts                                                          ← CSS Moduleを適用するための設定が書かれたファイル

Material UIを用いた各コンポーネント

index.tsx

Material UI適用前

index.tsx
import { createRoot } from "react-dom/client";
import App from "./components/App";

// as HTMLElementという型情報のコードを追加する
const rootElement = (document.getElementById("root") as HTMLElement);
const root = createRoot(rootElement);

root.render(<App />);

Material UI適用後

index.tsx
import { createRoot } from "react-dom/client";
import App from "./components/App";

// リセットCSSをインポートする
import "./css/reset.css";

// 共通CSSをインポートする
import "./css/common.css";

// as HTMLElementという型情報のコードを追加する
const rootElement = (document.getElementById("root") as HTMLElement);
const root = createRoot(rootElement);

root.render(<App />);

reset.css

reset.cssは、ChromeやFirefox、Safariなど各ブラウザによって異なるデフォルトのCSSを初期化し、ブラウザ間の表示を統一させるためのcssファイルです。
全体に適用するためにindex.tsxでインポートしておきます。

reset.css
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
    margin:0;
    padding:0;
    border:0;
    outline:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

body {
    line-height:1;
}

article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
    display:block;
}

nav ul {
    list-style:none;
}

blockquote, q {
    quotes:none;
}

blockquote:before, blockquote:after,
q:before, q:after {
    content:'';
    content:none;
}

a {
    margin:0;
    padding:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}

/* change colours to suit your needs */
ins {
    background-color:#ff9;
    color:#000;
    text-decoration:none;
}

/* change colours to suit your needs */
mark {
    background-color:#ff9;
    color:#000;
    font-style:italic;
    font-weight:bold;
}

del {
    text-decoration: line-through;
}

abbr[title], dfn[title] {
    border-bottom:1px dotted;
    cursor:help;
}

table {
    border-collapse:collapse;
    border-spacing:0;
}

/* change border colour to suit your needs */
hr {
    display:block;
    height:1px;
    border:0;  
    border-top:1px solid #cccccc;
    margin:1em 0;
    padding:0;
}

input, select {
    vertical-align:middle;
}

common.css

common.cssはファイル全体に共通で適用するCSSファイルです。
全体に適用するためにindex.tsxでインポートしておきます。

common.css
/* 入力フォーム */
.input {
    border: none;
    border-radius: 5px;
    background-color: rgba(0, 0, 0, 0.3);
    width: 95%;
    padding: 4% 2.5%;
    margin: 1.5% 0;
}

/* 入力フォーム(hover) */
.input:hover {
    animation-name: fadeInInput;
    animation-duration: .3s;
    animation-timing-function: ease-in-out;
    animation-fill-mode: forwards; 
}

/* 入力フォーム(focus) */
.input:focus {
    background-color: rgba(0, 0, 0, 0.1); 
}

/* 入力フォーム(focus-visible) */
.input:focus-visible {
    outline: none;
}

/* フェードインアニメーション */
.fadeIn {
    opacity: 0;
    animation-name: fadeIn; /* アニメーション名を指定 */
    animation-duration: .5s;   /* アニメーションの変化時間 */
    animation-timing-function: ease-in-out; /* アニメーションの進行具合を操作→開始時と終了時は、かなり緩やかに変化 */
    animation-fill-mode: forwards;  /* アニメーションの開始と終了時の状態を指定 forwards→元の状態に戻らずアニメーション最後の状態を維持 */
}

/* フェードインアニメーション */
@keyframes fadeIn {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

/* フェードインアニメーション(入力フォーム) */
@keyframes fadeInInput {
    0% {
        background-color: rgba(0, 0, 0, 0.3);
    }
    100% {
        background-color: rgba(0, 0, 0, 0.1);
    }
}

App.tsx

Material UI適用前

App.tsx
// useRefを利用できるようインポートする
import { useState } from "react";

// useTodo()カスタムフックをインポートする
import { useTodo } from "../hooks/useTodo";

// チェックリストの型をインポートする
import { CheckListType } from "../types"

// TodoTitleコンポーネントをインポートする
import TodoTitle from "./TodoTitle";

// TodoListコンポーネントをインポートする
import TodoList from "./TodoList";

// TodoAddコンポーネントをインポートする
import TodoAdd from "./TodoAddModal";

const App = () => {

  // useTodo()カスタムフックで作成したのを利用する
  // カスタムフックで定義したコンポーネントはApp.tsxで定義し、propsを用いて下の階層に渡す
  // 下の階層で定義すると親コンポーネント(App.tsx)がレンダリングされず処理は実行されているが画面は更新されない
  const {todoList, addTodoItem, toggleTodoItemStatus, changeTodoItem, deleteTodoItem} = useTodo();

  // 選択されたTODOリストのinputId、idを更新する関数setInputId
  const [id, setId] = useState<string>("");

  // 現在のタイトルの現在の状態変数inputTitle、inputTitleを更新する関数setInputTitle
  const [title, setTitle] = useState<string>("");

  // 現在のメモの現在の状態変数inputMemo、inputTitleを更新する関数setInputMemo
  const [memo, setMemo] = useState<string>("");

  // 現在のチェックリストの状態変数todoList、todoListを変更する関数setTodoList
  const [checkList, setCheckList] = useState<CheckListType>([]);

  // 現在の重要度の状態変数priority、priorityを更新する関数setPriority
  const [priority, setPriority] = useState<string>("");

  // 現在の難易度の状態変数difficulty、difficultyを更新する関数setDifficulty
  const [difficulty, setDifficulty] = useState<string>("");

  // 現在の期限の状態変数inputDeadLine、inputDeadLineを更新する関数setInputDeadLine
  const [deadLine, setDeadLine] = useState<Date>(new Date());

  // モーダルの表示の有無を設定する変数isShowModal、sShowModalを更新する関数setIsShowModal
  const [isShowModal, setIsShowModal] = useState<boolean>(false);

  // 追加ボタンと編集ボタンの変更を管理する変数changeFlg、changeFlgを更新する関数setChageFlg
  // False = 追加ボタン、True = 編集ボタン
  const [changeFlg, setChangeFlg] = useState<boolean>(false);

  // 漢字変換・予測変換(サジェスト)選択中か否かの判定
  // 変換中か否かの判定を行い、変換を確定させるエンターに反応しないように振り分ける
  // true=変換中、false=変換中ではない
  const [composing, setComposition] = useState<boolean>(false);

  /* 
  ##############################
  各State更新用の関数
  ############################## 
  */
  // id確認用の関数handleSetId
  const handleSetId = (id: string) => setId(id);

  // タイトル更新用の関数handleSetTitle
  const handleSetTitle = (title: string) => setTitle(title);

  // メモ更新用の関数handleSetMemo
  const handleSetMemo = (memo: string) => setMemo(memo);

  // チェックリスト更新用の関数handleSetCheckList
  const handleSetCheckList = (checkList: CheckListType) => setCheckList(checkList);

  // 重要度更新用の関数handleSetDifficulty
  const handleSetDifficulty = (difficulty: string) => setDifficulty(difficulty);

  // 難易度更新用の関数handleSetPriority
  const handleSetPriority = (priority: string) => setPriority(priority);

  // 期限更新用の関数handleSetDeadLine
  const handleSetDeadLine = (deadLine: Date) => setDeadLine(new Date(deadLine));

  // モーダル表示更新用の関数handleSetIsShowModal
  const handleSetIsShowModal = (isShowModal: boolean) => setIsShowModal(isShowModal);

  // 作成/編集更新用の関数handleSetChangeFlg
  const handleSetChangeFlg = (changeFlg: boolean) => setChangeFlg(changeFlg);

  // 変換開始
  const startComposition = () => setComposition(true);
  
  // 変換終了
  const endComposition = () => setComposition(false);
    
  /* 
  ##############################
  未完了のTODOリストを表示する
  ############################## 
  */
  // filter()メソッドを使用してTODOリスト内のdoneがfalseのTODOを取得する
  const inCompletedList = todoList.filter((todoItem) => {
    return !todoItem.done;
  });

  /* 
  ##############################
  完了済みのTODOリストを表示する
  ############################## 
  */
  // filter()メソッドを使用してTODOリスト内のdoneがfalseのTODOを取得する
  // 現在は使用していない 
  const completedList = todoList.filter((todoItem) => {
    return todoItem.done;
  });

  /* 
  ##############################
  リセット処理
  ############################## 
  */
  const reset = () => {
    setTitle("");
    setMemo("");
    setCheckList([]);
    setDifficulty("");
    setPriority("");
    setDeadLine(new Date());
  }

  /* 
  ##############################
  TODOリストの順番変更処理
  ############################## 
  */
  const reorder = (
    list: Array<any>, 
    startIndex: number, 
    endIndex: number) => {

    // Array.from()メソッドは、反復可能オブジェクトや配列風オブジェクトから
    // シャローコピーされた、新しいArrayインスタンスを生成する
    const result = Array.from(list);

    // Array.splice()メソッドは、配列を操作するメソッド
    // 第1引数には操作を開始する配列のインデックス、第1引数のみの場合、指定したインデックス以降を取り除く
    // 第2引数はオプション、第1引数に3、第2引数に1を指定した場合、3番目の要素を配列から取り出す
    const [removed] = result.splice(startIndex, 1);

    // 第3引数はオブション、第3引数に設定した値が配列に追加される
    result.splice(endIndex, 0, removed);
    return result;
  };

  /* 
  ##############################
  モーダルを非表示処理
  ############################## 
  */
  const closeModal = () => {
    setIsShowModal(false);
    setChangeFlg(!changeFlg);

    // 入力された内容をリセットする
    reset();
  };

  /* 
  ##############################
  TODOリスト簡易追加処理(エンターキーによる操作)
  ############################## 
  */
  const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, key: string) => {

    switch (key) {
    // エンターキーが押された際に以下の処理を実行する
    case "Enter":
        // input入力中にエンターを押すとデフォルトではsubmit(送信)になるため
        // e.preventDefault();で阻止する
        e.preventDefault();

        // 変換中ならそのまま何の処理も行わない
        if (composing) break;

        // 変換中でないなら、TODOを追加
        addTodoItem(
          title,
          memo,
          checkList,
          difficulty,
          priority,
          deadLine
        );

        // 追加後に入力フォームからフォーカスを外す
        (document.getElementById("simpleAddInput") as HTMLElement).blur();

        // 入力内容をクリアする
        setTitle("");

        break;
    default:
        break;
      }
  }

  return (
    <>
      <div>
        {/* TODOを作成するためのモーダルを表示する */}
        <button 
          onClick={() => {
          setIsShowModal(true); 
          setChangeFlg(false);
        }}
        >
          TODOの作成
        </button>
      </div>

      {/* onKeyDownでキーが押された際に処理を実行する */}
      {/* onCompositionStart/onCompositionEndで入力が確定しているかどうかを判断する */}
      <div>
        <input 
          type="text"
          id="simpleAddInput" 
          placeholder="TODOの追加"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          onKeyDown={(e) => onKeyDown(e, e.key)}
          onCompositionStart={startComposition}
          onCompositionEnd={endComposition}
        />
      </div>

      {/* 現在は使わないのでコメントアウト中 */}
      {/* <button>新しいパケットの追加</button> */}

      <div>
        <TodoTitle title="TODO" as="h2" />

        {/* TODOを追加するモーダルを表示する */}
        <TodoAdd 
          id={id}
          title={title}
          memo={memo}
          checkList={checkList}
          priority={priority}
          difficulty={difficulty}
          deadLine={deadLine}
          isShowModal={isShowModal}
          changeFlg={changeFlg}
          composing={composing}
          handleSetId={handleSetId}
          handleSetTitle={handleSetTitle}
          handleSetMemo={handleSetMemo}
          handleSetCheckList={handleSetCheckList}
          handleSetDifficulty={handleSetDifficulty}
          handleSetPriority={handleSetPriority}
          handleSetDeadLine={handleSetDeadLine}
          addTodoItem={addTodoItem} 
          toggleTodoItemStatus={toggleTodoItemStatus}
          changeTodoItem={changeTodoItem}
          deleteTodoItem={deleteTodoItem}
          closeModal={closeModal}
          startComposition={startComposition}
          endComposition={endComposition}
          reorder={reorder}
        />

        {/* 登録されたTODOリストを表示する */}
        <TodoList 
          todoList={inCompletedList}
          id={id}
          title={title}
          memo={memo}
          checkList={checkList}
          priority={priority}
          difficulty={difficulty}
          deadLine={deadLine}
          isShowModal={isShowModal}
          changeFlg={changeFlg}
          composing={composing}
          handleSetId={handleSetId}
          handleSetTitle={handleSetTitle}
          handleSetMemo={handleSetMemo}
          handleSetCheckList={handleSetCheckList}
          handleSetDifficulty={handleSetDifficulty}
          handleSetPriority={handleSetPriority}
          handleSetDeadLine={handleSetDeadLine}
          handleSetIsShowModal={handleSetIsShowModal}
          handleSetChangeFlg={handleSetChangeFlg}
          addTodoItem={addTodoItem} 
          toggleTodoItemStatus={toggleTodoItemStatus} 
          changeTodoItem={changeTodoItem}
          deleteTodoItem={deleteTodoItem}
          closeModal={closeModal}
          startComposition={startComposition}
          endComposition={endComposition}
          reorder={reorder}
        />
      </div>
    </>
  )
}

export default App;

Material UI適用後

App.tsx
// useRefを利用できるようインポートする
import { useState } from "react";

// useTodo()カスタムフックをインポートする
import { useTodo } from "../hooks/useTodo";

// チェックリストの型をインポートする
import { CheckListType, TodoListType } from "../types"

// TodoTitleコンポーネントをインポートする
import TodoTitle from "./TodoTitle";

// TodoListコンポーネントをインポートする
import TodoList from "./TodoList";

// TodoAddコンポーネントをインポートする
import TodoAdd from "./TodoAddModal";

// CSSファイルをインポートする
import AppStyle from "../css/AppStyle.module.css";

// Material UIのコンポーネントをインポートする
// Layout
import { Container, Box } from "@mui/material";

// Inputs
import { Button } from "@mui/material";

// Material Icons
import AddRoundedIcon from '@mui/icons-material/AddRounded';

const App = () => {

  // useTodo()カスタムフックで作成したのを利用する
  // カスタムフックで定義したコンポーネントはApp.tsxで定義し、propsを用いて下の階層に渡す
  // 下の階層で定義すると親コンポーネント(App.tsx)がレンダリングされず処理は実行されているが画面は更新されない
  const {todoList, setTodoList, addTodoItem, toggleTodoItemStatus, changeTodoItem, deleteTodoItem} = useTodo();

  // 選択されたTODOリストのinputId、idを更新する関数setInputId
  const [id, setId] = useState<string>("");

  // 現在のタイトルの現在の状態変数inputTitle、inputTitleを更新する関数setInputTitle
  const [title, setTitle] = useState<string>("");

  // 現在のメモの現在の状態変数inputMemo、inputTitleを更新する関数setInputMemo
  const [memo, setMemo] = useState<string>("");

  // 現在のチェックリストの状態変数todoList、todoListを変更する関数setTodoList
  const [checkList, setCheckList] = useState<CheckListType>([]);

  // 現在の重要度の状態変数priority、priorityを更新する関数setPriority
  const [priority, setPriority] = useState<string>("");

  // 現在の難易度の状態変数difficulty、difficultyを更新する関数setDifficulty
  const [difficulty, setDifficulty] = useState<string>("");

  // 現在の期限の状態変数inputDeadLine、inputDeadLineを更新する関数setInputDeadLine
  const [deadLine, setDeadLine] = useState<Date>(new Date());

  // モーダルの表示の有無を設定する変数isShowModal、sShowModalを更新する関数setIsShowModal
  const [isShowModal, setIsShowModal] = useState<boolean>(false);

  // 追加ボタンと編集ボタンの変更を管理する変数changeFlg、changeFlgを更新する関数setChageFlg
  // False = 追加ボタン、True = 編集ボタン
  const [changeFlg, setChangeFlg] = useState<boolean>(false);

  // 漢字変換・予測変換(サジェスト)選択中か否かの判定
  // 変換中か否かの判定を行い、変換を確定させるエンターに反応しないように振り分ける
  // true=変換中、false=変換中ではない
  const [composing, setComposition] = useState<boolean>(false);

  /* 
  ##############################
  各State更新用の関数
  ############################## 
  */

  // TODOリスト更新用の関数handleSetTodoList
  const handleSetTodoList = (todoList: TodoListType) => setTodoList(todoList);

  // id確認用の関数handleSetId
  const handleSetId = (id: string) => setId(id);

  // タイトル更新用の関数handleSetTitle
  const handleSetTitle = (title: string) => setTitle(title);

  // メモ更新用の関数handleSetMemo
  const handleSetMemo = (memo: string) => setMemo(memo);

  // チェックリスト更新用の関数handleSetCheckList
  const handleSetCheckList = (checkList: CheckListType) => setCheckList(checkList);

  // 重要度更新用の関数handleSetDifficulty
  const handleSetDifficulty = (difficulty: string) => setDifficulty(difficulty);

  // 難易度更新用の関数handleSetPriority
  const handleSetPriority = (priority: string) => setPriority(priority);

  // 期限更新用の関数handleSetDeadLine
  const handleSetDeadLine = (deadLine: Date) => setDeadLine(new Date(deadLine));

  // モーダル表示更新用の関数handleSetIsShowModal
  const handleSetIsShowModal = (isShowModal: boolean) => setIsShowModal(isShowModal);

  // 作成/編集更新用の関数handleSetChangeFlg
  const handleSetChangeFlg = (changeFlg: boolean) => setChangeFlg(changeFlg);

  // 変換開始
  const startComposition = () => setComposition(true);
  
  // 変換終了
  const endComposition = () => setComposition(false);
    
  /* 
  ##############################
  未完了のTODOリストを表示する
  ############################## 
  */
  // filter()メソッドを使用してTODOリスト内のdoneがfalseのTODOを取得する
  const inCompletedList = todoList.filter((todoItem) => {
    return !todoItem.done;
  });

  /* 
  ##############################
  完了済みのTODOリストを表示する
  ############################## 
  */
  // filter()メソッドを使用してTODOリスト内のdoneがfalseのTODOを取得する
  // 現在は使用していない 
  const completedList = todoList.filter((todoItem) => {
    return todoItem.done;
  });

  /* 
  ##############################
  リセット処理
  ############################## 
  */
  const reset = () => {
    setTitle("");
    setMemo("");
    setCheckList([]);
    setDifficulty("");
    setPriority("");
    setDeadLine(new Date());
  }

  /* 
  ##############################
  TODOリストの順番変更処理
  ############################## 
  */
  const reorder = (
    list: Array<any>, 
    startIndex: number, 
    endIndex: number) => {

    // Array.from()メソッドは、反復可能オブジェクトや配列風オブジェクトから
    // シャローコピーされた、新しいArrayインスタンスを生成する
    const result = Array.from(list);

    // Array.splice()メソッドは、配列を操作するメソッド
    // 第1引数には操作を開始する配列のインデックス、第1引数のみの場合、指定したインデックス以降を取り除く
    // 第2引数はオプション、第1引数に3、第2引数に1を指定した場合、3番目の要素を配列から取り出す
    const [removed] = result.splice(startIndex, 1);

    // 第3引数はオブション、第3引数に設定した値が配列に追加される
    result.splice(endIndex, 0, removed);
    return result;
  };

  /* 
  ##############################
  モーダルを非表示処理
  ############################## 
  */
  const closeModal = () => {
    setIsShowModal(false);
    setChangeFlg(!changeFlg);

    // 入力された内容をリセットする
    reset();
  };

  /* 
  ##############################
  TODOリスト簡易追加処理(エンターキーによる操作)
  ############################## 
  */
  const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, key: string) => {

    switch (key) {
    // エンターキーが押された際に以下の処理を実行する
    case "Enter":
        // input入力中にエンターを押すとデフォルトではsubmit(送信)になるため
        // e.preventDefault();で阻止する
        e.preventDefault();

        // 変換中ならそのまま何の処理も行わない
        if (composing) break;

        // 変換中でないなら、TODOを追加
        addTodoItem(
          title,
          memo,
          checkList,
          difficulty,
          priority,
          deadLine
        );

        // 追加後に入力フォームからフォーカスを外す
        (document.getElementById("simpleAddInput") as HTMLElement).blur();

        // 入力内容をクリアする
        setTitle("");

        break;
    default:
        break;
      }
  }

  return (
    <>
      <Box>
        {/* TODOを作成するためのモーダルを表示する */}
        <Button
          className={AppStyle.button} 
          variant="contained" // ボタンの背景を塗りつぶす
          onClick={() => {
          setIsShowModal(true); 
          setChangeFlg(false);
        }}
        >
          {/* sx={{...}}で直接CSSを適用する */}
          <AddRoundedIcon sx={{fontSize: 17.5 }}/>  
          TODOの作成
        </Button>
      </Box>

      <Container maxWidth="xs">
        <TodoTitle title="TODO" as="h1" />
        <Box className={AppStyle.TodoList}> 
          {/* onKeyDownでキーが押された際に処理を実行する */}
          {/* onCompositionStart/onCompositionEndで入力が確定しているかどうかを判断する */}
          <input
            className="input"
            type="text"
            id="simpleAddInput" 
            placeholder="TODOの追加"    
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            onKeyDown={(e) => onKeyDown(e, e.key)}
            onCompositionStart={startComposition}
            onCompositionEnd={endComposition}
          />

          {/* 現在は使わないのでコメントアウト中 */}
          {/* <button>新しいパケットの追加</button> */}

            {/* TODOを追加するモーダルを表示する */}
            <TodoAdd 
              id={id}
              title={title}
              memo={memo}
              checkList={checkList}
              priority={priority}
              difficulty={difficulty}
              deadLine={deadLine}
              isShowModal={isShowModal}
              changeFlg={changeFlg}
              composing={composing}
              handleSetId={handleSetId}
              handleSetTitle={handleSetTitle}
              handleSetMemo={handleSetMemo}
              handleSetCheckList={handleSetCheckList}
              handleSetDifficulty={handleSetDifficulty}
              handleSetPriority={handleSetPriority}
              handleSetDeadLine={handleSetDeadLine}
              addTodoItem={addTodoItem} 
              toggleTodoItemStatus={toggleTodoItemStatus}
              changeTodoItem={changeTodoItem}
              deleteTodoItem={deleteTodoItem}
              closeModal={closeModal}
              startComposition={startComposition}
              endComposition={endComposition}
              reorder={reorder}
            />

          {/* 登録されたTODOリストを表示する */}
          <TodoList 
            todoList={inCompletedList}
            id={id}
            title={title}
            memo={memo}
            checkList={checkList}
            priority={priority}
            difficulty={difficulty}
            deadLine={deadLine}
            isShowModal={isShowModal}
            changeFlg={changeFlg}
            composing={composing}
            handleSetTodoList={handleSetTodoList}
            handleSetId={handleSetId}
            handleSetTitle={handleSetTitle}
            handleSetMemo={handleSetMemo}
            handleSetCheckList={handleSetCheckList}
            handleSetDifficulty={handleSetDifficulty}
            handleSetPriority={handleSetPriority}
            handleSetDeadLine={handleSetDeadLine}
            handleSetIsShowModal={handleSetIsShowModal}
            handleSetChangeFlg={handleSetChangeFlg}
            addTodoItem={addTodoItem} 
            toggleTodoItemStatus={toggleTodoItemStatus} 
            changeTodoItem={changeTodoItem}
            deleteTodoItem={deleteTodoItem}
            closeModal={closeModal}
            startComposition={startComposition}
            endComposition={endComposition}
            reorder={reorder}
          />
        </Box>
      </Container>
    </>
  )
}

export default App;

AppStyle.module.css

AppStyle.module.css
/* TODOリスト全体 */
.TodoList {
    padding: 2.5%;
    border-radius: 5px;
    background-color: #edecee;
}

/* TODOの作成ボタン */
.button {
    margin: 2.5%;
}

TodoAddCheckItem.tsx

Material UI適用前

TodoAddCheckItem.tsx
// propsで渡される値の型定義を行う
type TodoAddCheckItemProps = {
  updateCheckList: (index: number, e: React.ChangeEvent<HTMLInputElement>) => void
  deleteCheckList: (index: number) => void
  checkItem : {
    checkItem: string
  }
  index: number
}

const TodoCheckItem = (props: TodoAddCheckItemProps) => {

  return (
    // チェックリストの各要素
    <>
      {/* 現在時点でチェックボックスは機能していない */}
      <input type="checkbox"></input>
      <input
        type="text"
        value={props.checkItem.checkItem}
        onChange={e => props.updateCheckList(props.index, e)}
      />
      <button onClick={() => props.deleteCheckList(props.index)}>削除</button>
    </>
  );
}

export default TodoCheckItem;

Material UI適用後

TodoAddCheckItem.tsx
import { useState } from "react";

import TodoAddCheckItemStyle from "../css/TodoAddCheckItemStyle.module.css";

// Material UI
// Inputs 
import { IconButton, InputAdornment, FormControl, Input, Checkbox } from "@mui/material";

// Layout
import { Box } from "@mui/material";

// Material icons
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
import DragIndicatorRoundedIcon from '@mui/icons-material/DragIndicatorRounded';

// propsで渡される値の型定義を行う
type TodoAddCheckItemProps = {
  updateCheckList: (index: number, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void
  deleteCheckList: (index: number) => void
  checkItem : {
    checkItem: string
  }
  index: number
}

const TodoCheckItem = (props: TodoAddCheckItemProps) => {

  // メニュー表示ボタンを表示する変数displayMenu、displayMuneを更新する関数setDisplayMenu
  const [displayBtn, setDisplayBtn] = useState<boolean>(false);

  return (
    // チェックリストの各要素
    <FormControl fullWidth>
      {/* 現在時点でチェックボックスは機能していない */}
      <Input
        className={TodoAddCheckItemStyle.input}
        type="text"
        value={props.checkItem.checkItem}
        onChange={e => props.updateCheckList(props.index, e)}
        onMouseEnter={() => setDisplayBtn(true)}
        onMouseLeave={() => setDisplayBtn(false)}
        startAdornment={
          <InputAdornment position="start">
            <Box className={TodoAddCheckItemStyle.icon}>
              { displayBtn && <DragIndicatorRoundedIcon /> }
            </Box>
            <Checkbox />
          </InputAdornment>
        }
        endAdornment={
          <InputAdornment position="end">
            <IconButton onClick={() => props.deleteCheckList(props.index)}>
              { displayBtn && <DeleteRoundedIcon/> }
            </IconButton>
          </InputAdornment>
        }
      />
    </FormControl>
  );
}

export default TodoCheckItem;
Inputタグ
      <Input
        startAdornment={
          <InputAdornment position="start">
            // 省略
          </InputAdornment>
        }
        endAdornment={
          <InputAdornment position="end">
            // 省略
          </InputAdornment>

startAdormentendAdornmentを指定することで要素を横に並べることが出てきまます。
startAdormentendAdornmentはどちらか片方のみの指定も可能です。

スクリーンショット 2023-05-13 10.19.34.png

TodoAddCheckItemStyle.module.css

TodoAddCheckItemStyle.module.css
/* チェックリスト */
.input {
    border-bottom: 1px solid rgba(0, 0, 0, 0.42);
}

/* チェックリスト(ホバー時) */
.input::before {
    display: none;
}

/* チェックリスト(クリック時) */
.input::after {
    display:none;
}

/* ドラッグ&ドロップ用アイコン */
.icon {
    position: absolute;
    top: 15%;
    left: -5%;
}

/* ドラッグ&ドロップ用アイコン(ホバー時) */
.icon:hover {
    cursor: pointer;
}

TodoAddCheckList.tsx

Material UI適用前

TodoAddCheckList.tsx
import React, { useState } from 'react';

// 一意なidを生成するulidをインポートする
import {ulid} from "ulid";

// ドラッグ&ドロップのライブラリreact-beautiful-dndをインポートする
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

// チェックリストの型をインポートする
import { CheckListType } from "../types"

import TodoCheckItem from "./TodoAddCheckItem";

// propsで渡される値の型定義を行う
type TodoCheckListProps = {
  composing: boolean
  reorder: (list: CheckListType, startIndex: number, endIndex: number) => any[]
  checkList: CheckListType
  handleSetCheckList: (checkList: CheckListType) => void
  startComposition: () => void
  endComposition: () => void
}

const TodoAddCheckList = (props: TodoCheckListProps) => {

  // チェックリストを追加するinputの現在の状態変数inputValue
  // inputValueを更新する関数setInputValueを定義する
  const [inputValue, setInputValue] = useState<string>("");

  /* 
  ##############################
  チェックリストのCSS
  ############################## 
  */
  // 引数:isDraggingOver を使用してドラッグ中とそうでない時のCSSを変更することができる
  const getListStyle = (isDraggingOver: boolean) => ({
    background: 'white',
    /* isDraggingOverの型は真偽値、true=ドラッグ中、false=ドラッグ中ではない  */
    /* border: isDraggingOver ? 'solid 5px lightgray' : 'solid 5px white', */
    textAlign: 'left',
  });

  /* 
  ##############################
  チェックアイテムのCSS
  ############################## 
  */
  const getItemStyle = (draggableStyle: any) => ({
    marginBottom: '0.5rem',

    ...draggableStyle
  });

  /* 
  ##############################
  チェックリスト追加処理(エンターキーによる操作)
  ############################## 
  */

  // エンターキーで新たなチェックリストを追加できるようにする
  const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>, key: string) => {

    switch (key) {
      // 変換中でない時にEnterキーでinputを増やす
      case "Enter":
        // input入力中にエンターを押すとデフォルトではsubmit(送信)になるため
        // e.preventDefault();で阻止する
        e.preventDefault();

        // 変換中ならそのまま何の処理も行わない
        if (props.composing) break;

        // 変換中でないなら、addCheckList()メソッドでチェックリストを追加
        addCheckList();

        break;
      default:
        break;
    }
  }

  /* 
  ##############################
  チェックリストの順番変更処理
  ############################## 
  */
  const onDragEnd = (result: any) => {

    // ドロップ先がない場合、そのまま処理を抜ける
    if (!result.destination) return;

    // 配列の順番を入れ替える
    const movedCheckItem = props.reorder(
      props.checkList,          // 順番を入れ替えたい配列
      result.source.index,      // 元の配列での位置
      result.destination.index  // 移動先の配列での位置
    );

    props.handleSetCheckList(movedCheckItem);
  };

  /* 
  ##############################
  チェックリスト追加処理
  ############################## 
  */
  const addCheckList = () => {

    // inputが空白ならそのまま何の処理も行わない
    if (inputValue === "") return;

    // 既存の配列に新たにチェックリストを加える
    // チェックリスト内要素の識別に使用されるidはstring(文字列)型でないと警告文が発生してしまう
    props.handleSetCheckList([...props.checkList, ...[{id: ulid(), checkItem: inputValue, done: false}]]);
    
    // チェックリストに追加した後、入力内容をクリアする
    setInputValue("");
  }

  /* 
  ##############################
  チェックリスト内容変更処理
  ############################## 
  */
  const updateCheckList = (index: number, e:React.ChangeEvent<HTMLInputElement>) => {

    // slice()メソッドを使用してチェックリストのコピーを作成する
    const copyCheckList = props.checkList.slice();

    // index を使用して対象のチェックリストの内容を書き換える
    copyCheckList[index].checkItem = e.target.value;
    props.handleSetCheckList(copyCheckList);
  }

  /* 
  ##############################
  チェックリスト削除処理
  ############################## 
  */
  const deleteCheckList = (index: number) => {
      
    // Array.from()メソッドは、反復可能オブジェクトや配列風オブジェクトから
    // シャローコピーされた、新しいArrayインスタンスを生成する
    const result = Array.from(props.checkList);

    // Array.splice()メソッドは、配列を操作するメソッド
    // 第2引数はオプション、第1引数に3、第2引数に2を指定した場合、3、4番目の要素を配列から取り出す
    result.splice(index, 1);
  
    props.handleSetCheckList(result);
  }

  return (
    // onDragEnd={onDragEnd}→ドラッグ後のイベント処理、タスクの状態や順番を変更する
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="droppable">
        {/* Droppableタグでsnapshotは以下のプロパティを持っている */}
        {/* snapshot.isDraggingOver:リスト上でアイテムがドラッグ中かどうか */}
        {(provided, snapshot) => (
          <div
            {...provided.droppableProps}
            ref={provided.innerRef}
            // Reactコンポーネント内でCSSを使用するとエラーが発生してしまうので as any をつける
            style={getListStyle(snapshot.isDraggingOver) as any}
          >
            {props.checkList.map((checkItem, index: number) => (
              <Draggable key={checkItem.id} draggableId={checkItem.id} index={index}>
                {/* Draggaleタグでsnapshotは以下のプロパティを持っている */}
                {/* snapshot.isDragging:アイテムがドラッグ中かどうか */}
                {(provided) => (
                  <div
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={getItemStyle(provided.draggableProps.style)}
                  >
                    <TodoCheckItem 
                      index={index}
                      checkItem={checkItem}
                      updateCheckList={updateCheckList}
                      deleteCheckList={deleteCheckList}
                    />
                  </div>
                )}
              </Draggable>
            ))}
            {/* ここにドラッグ可能なアイテムを配置 */}
            {provided.placeholder} 
            {/* 新しいチェックリストを追加するボタン/入力フォーム */}
            <button onClick={() => addCheckList()}>追加</button> 
            <input
              type="text" 
              value={inputValue}
              placeholder="新しいチェックリストを追加"
              onChange={(e) => setInputValue(e.target.value)}
              onKeyDown={(e) => onKeyDown(e, e.key)}
              onCompositionStart={props.startComposition}
              onCompositionEnd={props.endComposition}
            >
            </input>
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}

export default TodoAddCheckList;

Material UI適用後

TodoAddCheckList.tsx
import React, { useState } from 'react';

// 一意なidを生成するulidをインポートする
import {ulid} from "ulid";

// ドラッグ&ドロップのライブラリreact-beautiful-dndをインポートする
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

// チェックリストの型をインポートする
import { CheckListType } from "../types"

import TodoCheckItem from "./TodoAddCheckItem";

// Material UI
// Layout
import { Box } from "@mui/material";

// Inputs
import { IconButton, Input, InputAdornment } from "@mui/material";

// Material icons
import AddRoundedIcon from '@mui/icons-material/AddRounded';

// propsで渡される値の型定義を行う
type TodoCheckListProps = {
  composing: boolean
  reorder: (list: CheckListType, startIndex: number, endIndex: number) => any[]
  checkList: CheckListType
  handleSetCheckList: (checkList: CheckListType) => void
  startComposition: () => void
  endComposition: () => void
}

const TodoAddCheckList = (props: TodoCheckListProps) => {

  // チェックリストを追加するinputの現在の状態変数inputValue
  // inputValueを更新する関数setInputValueを定義する
  const [inputValue, setInputValue] = useState<string>("");

  /* 
  ##############################
  チェックリストのCSS
  ############################## 
  */
  // 引数:isDraggingOver を使用してドラッグ中とそうでない時のCSSを変更することができる
  const getListStyle = (isDraggingOver: boolean) => ({
    background: 'white',
    /* isDraggingOverの型は真偽値、true=ドラッグ中、false=ドラッグ中ではない  */
    /* border: isDraggingOver ? 'solid 5px lightgray' : 'solid 5px white', */
    textAlign: 'left',
  });

  /* 
  ##############################
  チェックアイテムのCSS
  ############################## 
  */
  const getItemStyle = (draggableStyle: any) => ({
    marginBottom: '0.5rem',

    ...draggableStyle
  });

  /* 
  ##############################
  チェックリスト追加処理(エンターキーによる操作)
  ############################## 
  */

  // エンターキーで新たなチェックリストを追加できるようにする
  const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>, key: string) => {

    switch (key) {
      // 変換中でない時にEnterキーでinputを増やす
      case "Enter":
        // input入力中にエンターを押すとデフォルトではsubmit(送信)になるため
        // e.preventDefault();で阻止する
        e.preventDefault();

        // 変換中ならそのまま何の処理も行わない
        if (props.composing) break;

        // 変換中でないなら、addCheckList()メソッドでチェックリストを追加
        addCheckList();

        break;
      default:
        break;
    }
  }

  /* 
  ##############################
  チェックリストの順番変更処理
  ############################## 
  */
  const onDragEnd = (result: any) => {

    // ドロップ先がない場合、そのまま処理を抜ける
    if (!result.destination) return;

    // 配列の順番を入れ替える
    const movedCheckItem = props.reorder(
      props.checkList,          // 順番を入れ替えたい配列
      result.source.index,      // 元の配列での位置
      result.destination.index  // 移動先の配列での位置
    );

    props.handleSetCheckList(movedCheckItem);
  };

  /* 
  ##############################
  チェックリスト追加処理
  ############################## 
  */
  const addCheckList = () => {

    // inputが空白ならそのまま何の処理も行わない
    if (inputValue === "") return;

    // 既存の配列に新たにチェックリストを加える
    // チェックリスト内要素の識別に使用されるidはstring(文字列)型でないと警告文が発生してしまう
    props.handleSetCheckList([...props.checkList, ...[{id: ulid(), checkItem: inputValue, done: false}]]);
    
    // チェックリストに追加した後、入力内容をクリアする
    setInputValue("");
  }

  /* 
  ##############################
  チェックリスト内容変更処理
  ############################## 
  */
  const updateCheckList = (index: number, e:React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {

    // slice()メソッドを使用してチェックリストのコピーを作成する
    const copyCheckList = props.checkList.slice();

    // index を使用して対象のチェックリストの内容を書き換える
    copyCheckList[index].checkItem = e.target.value;
    props.handleSetCheckList(copyCheckList);
  }

  /* 
  ##############################
  チェックリスト削除処理
  ############################## 
  */
  const deleteCheckList = (index: number) => {
      
    // Array.from()メソッドは、反復可能オブジェクトや配列風オブジェクトから
    // シャローコピーされた、新しいArrayインスタンスを生成する
    const result = Array.from(props.checkList);

    // Array.splice()メソッドは、配列を操作するメソッド
    // 第2引数はオプション、第1引数に3、第2引数に2を指定した場合、3、4番目の要素を配列から取り出す
    result.splice(index, 1);
  
    props.handleSetCheckList(result);
  }

  return (
    // onDragEnd={onDragEnd}→ドラッグ後のイベント処理、タスクの状態や順番を変更する
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="droppable">
        {/* Droppableタグでsnapshotは以下のプロパティを持っている */}
        {/* snapshot.isDraggingOver:リスト上でアイテムがドラッグ中かどうか */}
        {(provided, snapshot) => (
          <Box
            {...provided.droppableProps}
            ref={provided.innerRef}
            // Reactコンポーネント内でCSSを使用するとエラーが発生してしまうので as any をつける
            style={getListStyle(snapshot.isDraggingOver) as any}
          >
            {props.checkList.map((checkItem, index: number) => (
              <Draggable key={checkItem.id} draggableId={checkItem.id} index={index}>
                {/* Draggaleタグでsnapshotは以下のプロパティを持っている */}
                {/* snapshot.isDragging:アイテムがドラッグ中かどうか */}
                {(provided) => (
                  <Box
                    ref={provided.innerRef}
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    style={getItemStyle(provided.draggableProps.style)}
                  >
                    <TodoCheckItem 
                      index={index}
                      checkItem={checkItem}
                      updateCheckList={updateCheckList}
                      deleteCheckList={deleteCheckList}
                    />
                  </Box>
                )}
              </Draggable>
            ))}
            {/* ここにドラッグ可能なアイテムを配置 */}
            {provided.placeholder} 
            {/* 新しいチェックリストを追加するボタン/入力フォーム */}
            <Input
              type="text" 
              value={inputValue}
              placeholder="新しいチェックリストを追加"
              fullWidth
              onChange={(e) => setInputValue(e.target.value)}
              onKeyDown={(e) => onKeyDown(e, e.key)}
              onCompositionStart={props.startComposition}
              onCompositionEnd={props.endComposition}
              startAdornment={
                <InputAdornment position='start'>
                  <IconButton onClick={() => addCheckList()}>
                    <AddRoundedIcon />
                  </IconButton>
                </InputAdornment>
              }
            />
          </Box>
        )}
      </Droppable>
    </DragDropContext>
  );
}

export default TodoAddCheckList;

TodoAddModal.tsx

Material UI適用前

TodoAddModal.tsx
// モーダル表示するためにreact-modalをインポートする
import Modal from "react-modal";

// 期限を入力するためのライブラリとしてDatePickerをインポートする
import DatePicker from "react-datepicker";

// DatePickerで使用するカレンダーのCSSをインポートする
import "react-datepicker/dist/react-datepicker.css"

// バリデーションを行うためのreact-hook-formをインポートする
// 現時点では使用しない
import { useForm } from 'react-hook-form';

// チェックリストの型をインポートする
import { CheckListType } from "../types"

import TodoAddCheckList from "./TodoAddCheckList";

// propsで渡される値の型定義を行う
type TodoAddProps = {
    id: string
    title: string
    memo: string
    checkList: CheckListType
    priority: string
    difficulty: string
    deadLine: Date
    isShowModal: boolean
    changeFlg: boolean
    composing: boolean
    handleSetId: (id: string) => void
    handleSetTitle: (title: string) => void
    handleSetMemo: (memo: string) => void 
    handleSetCheckList: (checkList: CheckListType) => void
    handleSetDifficulty: (difficulty: string) => void
    handleSetPriority: (priority: string) => void
    handleSetDeadLine: (deadLine: Date) => void
    addTodoItem: (
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    toggleTodoItemStatus: (id: string, done: boolean) => void
    changeTodoItem: (
        id: string, 
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    deleteTodoItem: (id: string) => void
    closeModal: () => void
    startComposition: () => void
    endComposition: () => void
    reorder: (list: Array<any>, startIndex: number, endIndex: number) => any[]
}

// 重要度用の配列priorityItemsを定義する
const priorityItems = [
    {id: 1, value: ""},
    {id: 2, value: ""}
  ]
  
// 難易度用の配列difficultyItemsを定義する
const diffcultyItems = [
    {id: 1, value: ""},
    {id: 2, value: ""},
    {id: 3, value: ""}
] 

// モーダル画面のデザインを設定
const customStyles = {
    
    //モーダルの中身
    content: {
      width: "500px",
      height: "700px",
      top: "0",
      left: "0",
      right: "0",
      bottom: "0",
      margin: "auto",
      border: "none",
      padding: "30px 120px",
      background: "white",
    },

    //モーダルの外側の部分はoverlayを使用する
    overlay: {
      background: "rgba(62, 62, 62, 0.75)"
    }
};

// react-modalを使用するために宣言する必要あり
// 任意のアプリを設定する create-react-appなら#root
Modal.setAppElement("#root");

const TodoAddModal = (props: TodoAddProps) => {

    // react-hook-formの初期設定(現状は使用しない)
    // const { register, handleSubmit, watch, formState: { errors } } = useForm();

    /* 
    ##############################
    TODO編集処理
    ############################## 
    */
    const handleChangeTodoItem = () => {
        props.changeTodoItem(
            props.id,
            props.title,
            props.memo, 
            props.checkList, 
            props.priority, 
            props.difficulty, 
            props.deadLine
        );

        props.closeModal();
    }

    /* 
    ##############################
    TODO追加処理
    ############################## 
    */
    const handleAddTodoItem = () => {
        props.addTodoItem(
            props.title,
            props.memo, 
            props.checkList, 
            props.priority, 
            props.difficulty, 
            props.deadLine
        )

        props.closeModal();
    }

    return (
        <div>
            {/* モーダル表示したい部分をModalタグで囲む */}
            <Modal

                // モーダルをの表示処理isOpen
                // 表示/非表示はStateのisShowModalで管理する
                isOpen={props.isShowModal}

                // モーダルが表示された後の処理
                // モーダルが表示されている間、背景のスクロールを禁止する
                onAfterOpen={() => (document.getElementById("root") as HTMLElement).style.position = "fixed"}

                // モーダルが非表示になった後の処理
                // モーダルを閉じた後に画面スクロールできるようにする
                onAfterClose={() => (document.getElementById("root") as HTMLElement).style.position = "unset"}

                // ↓を記述するとモーダル画面の外側をクリックした際にモーダルが閉じる
                // onRequestClose={closeModal}
                
                // モーダルの中身/背景のデザインを設定する
                style={customStyles}
            >
                <div>
                    <div>
                        <p>タイトル*</p>
                        <input  
                            type="text" 
                            defaultValue={props.title} 
                            onChange={(e) => props.handleSetTitle(e.target.value)}
                        />
                    </div>
                    <div>
                        <p>メモ</p>
                        <textarea value={props.memo} onChange={(e) => props.handleSetMemo(e.target.value)}/>
                    </div>
                    <div>
                        <p>チェックリスト</p>
                            {/* チェックリストはコンポーネント化して別定義する */}
                            <TodoAddCheckList 
                                checkList={props.checkList} 
                                handleSetCheckList={props.handleSetCheckList}
                                reorder={props.reorder}
                                composing={props.composing}
                                startComposition={props.startComposition}
                                endComposition={props.endComposition}
                            />
                    </div>
                    <div>
                        <p>重要度</p>
                        {/* map()メソッドを使用して重要度用の配列priorityItemsから要素を取り出す */}
                        {priorityItems.map((priorityItem) => (
                            <label key={priorityItem.id}>
                                <input 
                                    type="radio" 
                                    value={priorityItem.value}
                                    onChange={(e) => props.handleSetPriority(e.target.value)}
                                    checked={props.priority === priorityItem.value}
                                />
                                {priorityItem.value}
                            </label>
                        ))}
                    </div>
                    <div>
                        <p>難易度</p>
                        {/* map()メソッドを使用して難易度用の配列difficultyItemsから要素を取り出す */}
                        <select defaultValue={props.difficulty} onChange={(e) => props.handleSetDifficulty(e.target.value)}>
                            {diffcultyItems.map((diffcultyItem) => (
                                <option key={diffcultyItem.id} value={diffcultyItem.value}>
                                    {diffcultyItem.value}
                                </option>
                            ))}
                        </select>
                    </div>
                    <div>
                        <p>期限</p>
                        {/* 期限はDatePickerを使用する */}
                        {/* slectedをで初期値を設定できる */}
                        {/* minDateを設定することで選択できる日付を制限できる */}
                        {/* minDate={new Date()}のように設定すると本日より前の日付は選択できないようになる */}
                        <DatePicker 
                            dateFormat="yyyy/MM/dd"
                            selected={props.deadLine}
                            onChange={(date) => props.handleSetDeadLine(date!)}
                            minDate={new Date()}
                        />
                    </div>
                    <div>
                        {/* changeFlgの値により表示するボタンを変更する */}
                        {props.changeFlg ? 
                            <button type="submit" onClick={handleChangeTodoItem}>編集する</button> :
                            <button type="submit" onClick={handleAddTodoItem}>作成する</button>
                        }
                        <button onClick={props.closeModal}>キャンセル</button>
                    </div>
                </div>
            </Modal>
        </div>
    );
}

export default TodoAddModal;

Material UI適用後

TodoAddModal.tsx
// モーダル表示するためにreact-modalをインポートする
import Modal from "react-modal";

// 期限を入力するためのライブラリとしてDatePickerをインポートする
import DatePicker from "react-datepicker";

// DatePickerで使用するカレンダーのCSSをインポートする
import "react-datepicker/dist/react-datepicker.css"

// バリデーションを行うためのreact-hook-formをインポートする
// 現時点では使用しない
import { useForm } from 'react-hook-form';

// チェックリストの型をインポートする
import { CheckListType } from "../types"

import TodoAddCheckList from "./TodoAddCheckList";

// 入力フォームに適応するCSSファイル
import "../css/common.css"

import TodoAddModalStyle from "../css/TodoAddModalStyle.module.css"

// Material UI
// Layout
import { Box } from "@mui/material";

// Inputs
import { Button, MenuItem, FormControl, Select, FormControlLabel, Radio } from "@mui/material";

// propsで渡される値の型定義を行う
type TodoAddProps = {
    id: string
    title: string
    memo: string
    checkList: CheckListType
    priority: string
    difficulty: string
    deadLine: Date
    isShowModal: boolean
    changeFlg: boolean
    composing: boolean
    handleSetId: (id: string) => void
    handleSetTitle: (title: string) => void
    handleSetMemo: (memo: string) => void 
    handleSetCheckList: (checkList: CheckListType) => void
    handleSetDifficulty: (difficulty: string) => void
    handleSetPriority: (priority: string) => void
    handleSetDeadLine: (deadLine: Date) => void
    addTodoItem: (
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    toggleTodoItemStatus: (id: string, done: boolean) => void
    changeTodoItem: (
        id: string, 
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    deleteTodoItem: (id: string) => void
    closeModal: () => void
    startComposition: () => void
    endComposition: () => void
    reorder: (list: Array<any>, startIndex: number, endIndex: number) => any[]
}

// 重要度用の配列priorityItemsを定義する
const priorityItems = [
    {id: 1, value: ""},
    {id: 2, value: ""}
  ]
  
// 難易度用の配列difficultyItemsを定義する
const diffcultyItems = [
    {id: 1, value: ""},
    {id: 2, value: ""},
    {id: 3, value: ""}
] 

// モーダル画面のデザインを設定
const customStyles = {
    
    //モーダルの中身
    content: {
      width: "500px",
      height: "700px",
      top: "0",
      left: "0",
      right: "0",
      bottom: "0",
      margin: "auto",
      border: "none",
      padding: "30px 120px",
      background: "white",
    },

    //モーダルの外側の部分はoverlayを使用する
    overlay: {
      background: "rgba(62, 62, 62, 0.75)"
    }
};

// react-modalを使用するために宣言する必要あり
// 任意のアプリを設定する create-react-appなら#root
Modal.setAppElement("#root");

const TodoAddModal = (props: TodoAddProps) => {

    // react-hook-formの初期設定(現状は使用しない)
    // const { register, handleSubmit, watch, formState: { errors } } = useForm();

    /* 
    ##############################
    TODO編集処理
    ############################## 
    */
    const handleChangeTodoItem = () => {
        props.changeTodoItem(
            props.id,
            props.title,
            props.memo, 
            props.checkList, 
            props.priority, 
            props.difficulty, 
            props.deadLine
        );

        props.closeModal();
    }

    /* 
    ##############################
    TODO追加処理
    ############################## 
    */
    const handleAddTodoItem = () => {
        props.addTodoItem(
            props.title,
            props.memo, 
            props.checkList, 
            props.priority, 
            props.difficulty, 
            props.deadLine
        )

        props.closeModal();
    }

    return (
        <Box>
            {/* モーダル表示したい部分をModalタグで囲む */}
            <Modal
                // モーダルをの表示処理isOpen
                // 表示/非表示はStateのisShowModalで管理する
                isOpen={props.isShowModal}

                // モーダルが表示された後の処理
                // モーダルが表示されている間、背景のスクロールを禁止する
                onAfterOpen={() => (document.getElementById("root") as HTMLElement).style.position = "fixed"}

                // モーダルが非表示になった後の処理
                // モーダルを閉じた後に画面スクロールできるようにする
                onAfterClose={() => (document.getElementById("root") as HTMLElement).style.position = "unset"}

                // ↓を記述するとモーダル画面の外側をクリックした際にモーダルが閉じる
                // onRequestClose={closeModal}
                
                // モーダルの中身/背景のデザインを設定する
                style={customStyles}
            >
                <Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>タイトル*</h3>
                        <input
                            // 2つのクラスを適用する
                            className={`input ${TodoAddModalStyle.input}`}
                            type="text"
                            defaultValue={props.title} 
                            onChange={(e) => props.handleSetTitle(e.target.value)}
                        />
                    </Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>メモ</h3>
                        <textarea 
                            // 2つのクラスを適用する
                            className={`input ${TodoAddModalStyle.textarea}`}
                            value={props.memo} 
                            onChange={(e) => props.handleSetMemo(e.target.value)}
                        />
                    </Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>チェックリスト</h3>
                            {/* チェックリストはコンポーネント化して別定義する */}
                            <TodoAddCheckList 
                                checkList={props.checkList} 
                                handleSetCheckList={props.handleSetCheckList}
                                reorder={props.reorder}
                                composing={props.composing}
                                startComposition={props.startComposition}
                                endComposition={props.endComposition}
                            />
                    </Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>重要度</h3>
                        {/* map()メソッドを使用して重要度用の配列priorityItemsから要素を取り出す */}
                        {priorityItems.map((priorityItem) => (
                            <FormControlLabel
                                key={priorityItem.id} 
                                value={priorityItem.value}
                                control={
                                    <Radio
                                        onChange={(e) => props.handleSetPriority(e.target.value)}
                                        checked={props.priority === priorityItem.value} 
                                    />
                                }
                                label={priorityItem.value}
                            />
                        ))}
                    </Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>難易度</h3>
                        <FormControl fullWidth size="small">
                        {/* map()メソッドを使用して難易度用の配列difficultyItemsから要素を取り出す */}
                            <Select
                                className={TodoAddModalStyle.select} 
                                defaultValue={props.difficulty} 
                                onChange={(e) => props.handleSetDifficulty(e.target.value)}
                            >
                                {diffcultyItems.map((diffcultyItem) => (
                                    <MenuItem key={diffcultyItem.id} value={diffcultyItem.value}>
                                        {diffcultyItem.value}
                                    </MenuItem>
                                ))}
                            </Select>
                        </FormControl>
                    </Box>
                    <Box className={TodoAddModalStyle.InputForm}>
                        <h3 className={TodoAddModalStyle.title}>期限</h3>
                        {/* 期限はDatePickerを使用する */}
                        {/* slectedをで初期値を設定できる */}
                        {/* minDateを設定することで選択できる日付を制限できる */}
                        {/* minDate={new Date()}のように設定すると本日より前の日付は選択できないようになる */}
                        <DatePicker 
                            className={TodoAddModalStyle.input}
                            dateFormat="yyyy/MM/dd"
                            selected={props.deadLine}
                            onChange={(date) => props.handleSetDeadLine(date!)}
                            minDate={new Date()}
                        />
                    </Box>
                    <Box>
                        {/* changeFlgの値により表示するボタンを変更する */}
                        {props.changeFlg ? 
                            <Button variant="contained" type="submit" onClick={handleChangeTodoItem}>編集する</Button> :
                            <Button variant="contained" type="submit" onClick={handleAddTodoItem}>作成する</Button>
                        }
                        <Button className={TodoAddModalStyle.button} variant="contained" onClick={props.closeModal}>キャンセル</Button>
                    </Box>
                </Box>
            </Modal>
        </Box>
    );
}

export default TodoAddModal;

TodoAddModalStyle.module.css

TodoAddModalStyle.module.css
/* テキスト入力フォーム(モーダル画面) */
.input {
    padding: 2.5%;
}

/* テキスト入力フォーム(メモ) */
.textarea {
    height: 100px
}

/* テキストエリア入力フォーム(focus-visible) */
.textarea:focus-visible {
    outline: none;
}

/* 各入力フォーム */
.InputForm {
    margin-bottom: 5%;
}

/* 入力フォームタイトル */
.title {
    margin-bottom: 2.5%;
}

/* ボタン */
.button {
    margin-left: 5%;
}

TodoItem.tsx

Material UI適用前

TodoItem.tsx
import { useState, useEffect } from "react";

// TODOリスト、チェックリストの型をインポートする
import { TodoItemType, CheckListType } from "../types"

import TodoAddModal from "./TodoAddModal";

type TodoItemProps = {
    todoItem: TodoItemType
    id: string
    title: string
    memo: string
    checkList: CheckListType
    priority: string
    difficulty: string
    deadLine: Date
    isShowModal: boolean
    changeFlg: boolean
    composing: boolean
    handleSetId: (id: string) => void
    handleSetTitle: (title: string) => void
    handleSetMemo: (memo: string) => void 
    handleSetCheckList: (checkList: CheckListType) => void
    handleSetDifficulty: (difficulty: string) => void
    handleSetPriority: (priority: string) => void
    handleSetDeadLine: (deadLine: Date) => void
    handleSetIsShowModal: (isShowModal: boolean) => void
    handleSetChangeFlg: (changeFlg: boolean) => void
    addTodoItem: (
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    toggleTodoItemStatus: (id: string, done: boolean) => void
    changeTodoItem: (
        id: string, 
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    deleteTodoItem: (id: string) => void
    closeModal: () => void
    startComposition: () => void
    endComposition: () => void
    reoreder: (list: Array<any>, startIndex: number, endIndex: number) => any[]
}

const TodoItem = (props: TodoItemProps) => {

    // 期限までの時間を設定する変数deadLine
    // deadLineを更新する関数setDeadLineを定義する
    const [deadLine, setDeadLine] = useState("");

    /* 
    ##############################
    モーダル表示処理
    ############################## 
    */
    const handleChangeTodo = () => {

        props.handleSetId(props.todoItem.id!);                  // 対象のTODOからIDを取得する
        props.handleSetTitle(props.todoItem.title!);            // 対象のTODOからタイトルを取得する
        props.handleSetMemo(props.todoItem.memo!);              // 対象のTODOからメモを取得する
        props.handleSetCheckList(props.todoItem.checkList!);    // 対象のTODOからチェックリストを取得する
        props.handleSetDifficulty(props.todoItem.difficulty!);  // 対象のTODOから難易度を取得する
        props.handleSetPriority(props.todoItem.priority!);      // 対象のTODOから重要度を取得する
        props.handleSetDeadLine(props.todoItem.deadLine!);      // 対象のTODOから期限を取得する

        // 編集ボタンを表示するよう変更する
        props.handleSetChangeFlg(true);

        // モーダルを表示する
        props.handleSetIsShowModal(true);   
    }

    /* 
    ##############################
    期限までの残り時間を表示する
    ############################## 
    */
    const handleCountDown = () => {

        const nowDate = new Date();                                     // 本日の日時を取得
        const deadLineDate = new Date(props.todoItem.deadLine!);        // 期限の日時を取得
        const diffDate = deadLineDate.getTime() - nowDate.getTime();    // 期限までの残り時間を取得

        // 期限が過ぎていない場合
        if (diffDate > 0) {
            setDeadLine(`${Math.floor(diffDate / 1000 / 60 / 60 / 24)}日後`);
        // 期限が今日の場合
        } else if (diffDate === 0) {
            setDeadLine("今日");
        // 期限が過ぎている場合
        } else {
            setDeadLine("期限切れ")
        }
    } 

    // useEffectを使用してコンポーネントのマウント後に関数handleCountDownを実行する
    // useEffectの第2引数にprops.todoItem.deadLineを設定することでprops.todoItem.deadLineが
    // 更新される度に関数handleCountDownを実行する
    useEffect(handleCountDown, [props.todoItem.deadLine]);

    /* 
    ##############################
    チェックボックス更新処理
    ############################## 
    */
    // 現在時点で関数handleChangeCheckは機能していない
    const handleChangeCheck = () => {

    };

    return (
        <div>
            <h3>{props.todoItem.title}</h3>
            <input type="hidden"></input>
            <p>{props.todoItem.memo}</p>
            {/* map()を利用してcheckListの要素を1つひとつ取り出す */}
            {/* map()メソッドを使用する対象の配列に「! = undifined」を設定するとエラー */}
            {props.todoItem.checkList?.map((checkItem) => (
                <label key={checkItem.id} style={{display: "block"}}>
                    <input type="checkbox" value={checkItem.checkItem} onChange={handleChangeCheck}/>
                    {checkItem.checkItem}
                </label>
            ))}
            <p>期限: 
                {/* 最初にタスクの完了/未完了を判定する、その後期限が過ぎていないか判定する */}
                {/* 上記の条件をクリアした場合、期限までの時間を表示する */}
                {props.todoItem.done ? "完了済み" : deadLine}
            </p>

            {/* ボタンをクリックすることで関数handleToggleTodoItemStatusを実行する */}
            {/* ボタンをクリックすることでTODOの状態(完了/未完了)を反転させる */}
            <button onClick={() => {props.toggleTodoItemStatus(props.todoItem.id!, props.todoItem.done!)}}>
                {props.todoItem.done ? "未完了リストへ" : "完了リストへ"}
            </button>

            {/* ボタンをクリックすることで関数handleDeleteTodoItemを実行する */}
            {/* ボタンをクリックすることでTODOを削除する */}
            <button onClick={() => {props.deleteTodoItem(props.todoItem.id!)}}>削除</button>

            {/* ボタンをクリックすることで関数handleChangeTodoItemを実行する */}
            {/* 関数handleChangeTodoItemが実行されるとモーダル画面が表示される */}
            <button onClick={handleChangeTodo}>編集</button>
            {props.isShowModal && 
                <TodoAddModal 
                    id={props.id}
                    title={props.title}
                    memo={props.memo}
                    checkList={props.checkList}
                    priority={props.priority}
                    difficulty={props.difficulty}
                    deadLine={props.deadLine}
                    isShowModal={props.isShowModal}
                    changeFlg={props.changeFlg}
                    composing={props.composing}
                    handleSetId={props.handleSetId}
                    handleSetTitle={props.handleSetTitle}
                    handleSetMemo={props.handleSetMemo}
                    handleSetCheckList={props.handleSetCheckList}
                    handleSetDifficulty={props.handleSetDifficulty}
                    handleSetPriority={props.handleSetPriority}
                    handleSetDeadLine={props.handleSetDeadLine}
                    addTodoItem={props.addTodoItem} 
                    toggleTodoItemStatus={props.toggleTodoItemStatus} 
                    changeTodoItem={props.changeTodoItem}
                    deleteTodoItem={props.deleteTodoItem}
                    closeModal={props.closeModal}
                    startComposition={props.startComposition}
                    endComposition={props.endComposition}
                    reorder={props.reoreder}
                />
            }
        </div>
    );
}

export default TodoItem;

Material UI適用後

TodoItem.tsx
import { useState, useEffect } from "react";

// TODOリスト、チェックリストの型をインポートする
import { TodoItemType, CheckListType } from "../types"

import TodoAddModal from "./TodoAddModal";

import TodoItemStyle from "../css/TodoItemStyle.module.css"

// Material UI
// Layout
import { Box } from "@mui/material";

// Inputs
import { IconButton, Menu, MenuItem, Tooltip, FormGroup, FormControlLabel, Checkbox } from "@mui/material";

// Data Display
import { List, ListItemButton, ListItemText, Collapse } from "@mui/material"; 

// Material icons
import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded';
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
import EditRoundedIcon from '@mui/icons-material/EditRounded';
import FormatListBulletedRoundedIcon from '@mui/icons-material/FormatListBulletedRounded';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';

type TodoItemProps = {
    todoItem: TodoItemType
    id: string
    title: string
    memo: string
    checkList: CheckListType
    priority: string
    difficulty: string
    deadLine: Date
    isShowModal: boolean
    changeFlg: boolean
    composing: boolean
    handleSetId: (id: string) => void
    handleSetTitle: (title: string) => void
    handleSetMemo: (memo: string) => void 
    handleSetCheckList: (checkList: CheckListType) => void
    handleSetDifficulty: (difficulty: string) => void
    handleSetPriority: (priority: string) => void
    handleSetDeadLine: (deadLine: Date) => void
    handleSetIsShowModal: (isShowModal: boolean) => void
    handleSetChangeFlg: (changeFlg: boolean) => void
    addTodoItem: (
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    toggleTodoItemStatus: (id: string, done: boolean) => void
    changeTodoItem: (
        id: string, 
        title: string, 
        memo: string, 
        checkList: CheckListType, 
        priority: string, 
        difficulty: string, 
        deadLine: Date
    ) => void
    deleteTodoItem: (id: string) => void
    closeModal: () => void
    startComposition: () => void
    endComposition: () => void
    reoreder: (list: Array<any>, startIndex: number, endIndex: number) => any[]
}

const TodoItem = (props: TodoItemProps) => {

    // 期限までの時間を設定する変数deadLine、deadLineを更新する関数setDeadLine
    const [deadLine, setDeadLine] = useState("");

    // メニュー表示ボタンを表示する変数displayMenu、displayMuneを更新する関数setDisplayMenu
    const [displayMenu, setDisplayMenu] = useState<boolean>(false);

    // 現在のメニュー表示の状態変数anchorMenu、anchorElを更新する関数setAnchorMenu
    const [anchorMenu, setAnchorMenu] = useState<null>(null);

    // 現在のメニュー表示の状態変数anchorMenu、anchorElを更新する関数setAnchorMenu
    const [anchorList, setAnchorList] = useState<boolean>(false);

    // メニュー非表示処理
    const handleClose = () => setAnchorMenu(null);


    /* 
    ##############################
    期限までの残り時間を表示する
    ############################## 
    */
    const handleCountDown = () => {

        const nowDate = new Date();                                     // 本日の日時を取得
        const deadLineDate = new Date(props.todoItem.deadLine!);        // 期限の日時を取得
        const diffDate = deadLineDate.getTime() - nowDate.getTime();    // 期限までの残り時間を取得

        // 期限が過ぎていない場合
        if (diffDate > 0) {
            setDeadLine(`${Math.floor(diffDate / 1000 / 60 / 60 / 24)}日後`);
        // 期限が今日の場合
        } else if (diffDate === 0) {
            setDeadLine("今日");
        // 期限が過ぎている場合
        } else {
            setDeadLine("期限切れ")
        }
    } 

    // useEffectを使用してコンポーネントのマウント後に関数handleCountDownを実行する
    // useEffectの第2引数にprops.todoItem.deadLineを設定することでprops.todoItem.deadLineが
    // 更新される度に関数handleCountDownを実行する
    useEffect(handleCountDown, [props.todoItem.deadLine]);

    /* 
    ##############################
    完了/未完了変更処理
    ############################## 
    */
    const handletoggleTodo = () => {
        props.toggleTodoItemStatus(props.todoItem.id!, props.todoItem.done!);

        // メニューを非表示にする
        handleClose();
    }

    /* 
    ##############################
    TODO削除処理
    ############################## 
    */
    const handleDeleteTodo = () => {
        
        // 実行確認ダイアログを表示する
        const checkFlg = window.confirm("本当に削除してもよろしいですか");

        // 「はい」が押された場合、削除処理を実行する
        if (checkFlg) props.deleteTodoItem(props.todoItem.id!);

        // メニューを非表示にする
        handleClose();
    }

    /* 
    ##############################
    モーダル表示処理
    ############################## 
    */
    const handleChangeTodo = () => {

        props.handleSetId(props.todoItem.id!);                  // 対象のTODOからIDを取得する
        props.handleSetTitle(props.todoItem.title!);            // 対象のTODOからタイトルを取得する
        props.handleSetMemo(props.todoItem.memo!);              // 対象のTODOからメモを取得する
        props.handleSetCheckList(props.todoItem.checkList!);    // 対象のTODOからチェックリストを取得する
        props.handleSetDifficulty(props.todoItem.difficulty!);  // 対象のTODOから難易度を取得する
        props.handleSetPriority(props.todoItem.priority!);      // 対象のTODOから重要度を取得する
        props.handleSetDeadLine(props.todoItem.deadLine!);      // 対象のTODOから期限を取得する

        // 編集ボタンを表示するよう変更する
        props.handleSetChangeFlg(true);

        // モーダルを表示する
        props.handleSetIsShowModal(true);

        // メニューを非表示にする
        handleClose();
    }

    /* 
    ##############################
    チェックボックス更新処理
    ############################## 
    */
    // 現在時点で関数handleChangeCheckは機能していない
    const handleChangeCheck = () => {
        
    };

    return (
        // TODOアイテム
        <Box
            className={TodoItemStyle.TodoItem}
            onMouseEnter={() => setDisplayMenu(true)}
            onMouseLeave={() => setDisplayMenu(false)}
        >
            {/* TODOチェックボックス */}
            <Box className={TodoItemStyle.TodoItemLeft}>
                {/* チェックボックスをクリックすることで関数handleToggleTodoItemStatusを実行する */}
                {/* ボタンをクリックすることでTODOの状態(完了/未完了)を反転させる */}
                <Checkbox 
                    className={TodoItemStyle.checkbox}
                    onChange={handletoggleTodo} 
                />
            </Box>
            {/* TODOの内容 */}
            <Box className={TodoItemStyle.TodoItemRight}>
                <p className={TodoItemStyle.todoItemTitle}>{props.todoItem.title}</p>

                {displayMenu && 
                    <Box className={`fadeIn ${TodoItemStyle.menu}`}>
                        <IconButton onClick={(e: any) => setAnchorMenu(e.currentTarget)}>
                            <Tooltip title="オプション" placement="top">
                                <MoreVertRoundedIcon />
                            </Tooltip>
                        </IconButton>
                        <Menu
                            anchorEl={anchorMenu}
                            open={Boolean(anchorMenu)}
                            onClose={() =>setAnchorMenu(null)}
                            PaperProps={{ style: { width: '200px' }}}
                        >
                            {/* ボタンをクリックすることで関数handleDeleteTodoItemを実行する */}
                            {/* ボタンをクリックすることでTODOを削除する */}
                            <MenuItem onClick={handleDeleteTodo}>
                                <DeleteRoundedIcon /> 
                                削除
                            </MenuItem>

                            {/* ボタンをクリックすることで関数handleChangeTodoItemを実行する */}
                            {/* 関数handleChangeTodoItemが実行されるとモーダル画面が表示される */}
                            <MenuItem onClick={handleChangeTodo}>
                                <EditRoundedIcon /> 
                                編集
                            </MenuItem>
                        </Menu>
                    </Box>
                }

                <p className={TodoItemStyle.todoItemMemo}>{props.todoItem.memo}</p>

                <ListItemButton className={TodoItemStyle.list} onClick={() => setAnchorList(!anchorList)}>
                    <FormatListBulletedRoundedIcon className={TodoItemStyle.listIcon} />
                    <ListItemText className={TodoItemStyle.listText} primary="チェックリスト" />
                    {anchorList ? <ExpandMore className={TodoItemStyle.listToggleIcon} /> : <ExpandLess className={TodoItemStyle.listToggleIcon} />}
                </ListItemButton>
                <Collapse in={anchorList}>
                    <List>   
                        {/* map()を利用してcheckListの要素を1つひとつ取り出す */}
                        {/* map()メソッドを使用する対象の配列に「! = undifined」を設定するとエラー */}
                        {props.todoItem.checkList?.map((checkItem) => (
                            <FormGroup key={checkItem.id} style={{display: "block"}}>
                                <FormControlLabel 
                                    className={TodoItemStyle.checkItem}
                                    control={
                                        <Checkbox 
                                            value={checkItem.checkItem} 
                                            onChange={handleChangeCheck}
                                        />
                                    } 
                                    label={checkItem.checkItem} 
                                />
                            </FormGroup>
                        ))}
                    </List>
                </Collapse>
                <p className={TodoItemStyle.deadLine}>期限: 
                    {/* 最初にタスクの完了/未完了を判定する、その後期限が過ぎていないか判定する */}
                    {/* 上記の条件をクリアした場合、期限までの時間を表示する */}
                    {props.todoItem.done ? "完了済み" : deadLine}
                </p>
            </Box>
                {props.isShowModal && 
                    <TodoAddModal 
                        id={props.id}
                        title={props.title}
                        memo={props.memo}
                        checkList={props.checkList}
                        priority={props.priority}
                        difficulty={props.difficulty}
                        deadLine={props.deadLine}
                        isShowModal={props.isShowModal}
                        changeFlg={props.changeFlg}
                        composing={props.composing}
                        handleSetId={props.handleSetId}
                        handleSetTitle={props.handleSetTitle}
                        handleSetMemo={props.handleSetMemo}
                        handleSetCheckList={props.handleSetCheckList}
                        handleSetDifficulty={props.handleSetDifficulty}
                        handleSetPriority={props.handleSetPriority}
                        handleSetDeadLine={props.handleSetDeadLine}
                        addTodoItem={props.addTodoItem} 
                        toggleTodoItemStatus={props.toggleTodoItemStatus} 
                        changeTodoItem={props.changeTodoItem}
                        deleteTodoItem={props.deleteTodoItem}
                        closeModal={props.closeModal}
                        startComposition={props.startComposition}
                        endComposition={props.endComposition}
                        reorder={props.reoreder}
                    />
                }
        </Box>
    );
}

export default TodoItem;

TodoItemStyle.module.css

TodoItemStyle.module.css
/* TODOアイテム全体 */
.TodoItem {
    border-radius: 5px;
    display: flex;
    justify-content: space-between;
    align-items: stretch;
}

/* TODOアイテム全体(ホバー時) */
.TodoItem:hover {
    border: 1px solid #2665c0;
}

/* TODOアイテム左側(チェックボックス) */
.TodoItemLeft {
    border-radius: 5px 0 0 5px;
    width: 15%;
    background-color: #50b5e9;
}

/* チェックボックス */
.checkbox svg {
    width: 100%;
    height: 100%;
}

/* TODOアイテム右側(内容) */
.TodoItemRight {
    position: relative;
    width: 85%;
    padding: 10px;
}

/* メニューボタン */
.menu {
    position: absolute;
    top: 0;
    right: 0;
}

/* TODOアイテムタイトル */
.todoItemTitle {
    color: #7a787e;
    font-size: 18px;
}

/* TODOアイテムメモ */
.todoItemMemo {
    font-size: 12px;
    color: #BBB9C1;
    margin-top: 5%;
}

/* TODOアイテムチェックリスト */
.list {
    padding: unset !important;
    margin: 2.5% 0 !important;
}

/* TODOアイテムチェックリスト(要素) */
.listText {
    flex: none !important;
}

/* TODOアイテムチェックリスト(テキスト) */
.listText span {
    font-size: 14px;
    color: #7a787e;
}

/* TODOチェックリストアイコン */
.listIcon {
    width: 5% !important;
    height: 5% !important; 
    padding-right: 2.5%;
}

/* TODOチェックリストトグルアイコン */
.listToggleIcon {
    width: 7.5%;
    height: 7.5%;
}

/* TODOチェックアイテム */
.checkItem {
    margin: -2.5% 0;
    font-size: 14px;
    color: #7a787e;
}

/* TODOアイテム期限 */
.deadLine {
    font-size: 14px;
    color: #7a787e;
}

TodoTitle.tsx

Material UI適用前

TodoTitle.tsx
// TodoTitleコンポーネントを作成する
// 親コンポーネント(App)から受け取ったprops(as)の値により使用するタグを変更

// propsで渡される値の型定義を行う
type TodoTitleProps = {
  title: string
  as : string
}

const TodoTitle = (props: TodoTitleProps) => {

    // asがh1ならば、タイトルはh1タグを使用
    if (props.as === "h1") {
      return <h1>{props.title}</h1>
  
    // asがh2ならば、タイトルはh2タグを使用
    } else if (props.as === "h2") {
      return <h2>{props.title}</h2>
  
    // それ以外ならば、タイトルはpタグを使用
    } else {
      return <p>{props.title}</p>
    }
  }

export default TodoTitle;

Material UI適用後

TodoTitle.tsx
// cssファイルをインポートする
import TodoTitleStyle from "../css/TodoTitleStyle.module.css"

// propsで渡される値の型定義を行う
type TodoTitleProps = {
  title: string
  as : string
}

const TodoTitle = (props: TodoTitleProps) => {

    // asがh1ならば、タイトルはh1タグを使用
    if (props.as === "h1") {
      return <h1 className={TodoTitleStyle.title}>{props.title}</h1>
  
    // asがh2ならば、タイトルはh2タグを使用
    } else if (props.as === "h2") {
      return <h2>{props.title}</h2>
  
    // それ以外ならば、タイトルはpタグを使用
    } else {
      return <p>{props.title}</p>
    }
  }

export default TodoTitle;

TodoTitleStyle.module.css

TodoTitleStyle.module.css
/* タイトル */
.title {
    font-size: 24px;
    color: #34313a;
    margin-bottom: 2.5%;   
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?