タスク管理ツール（TypeScript）

# タスク管理ツール
初めての開発なのでReactは使わない方針でいく
環境設定は[コチラ](https://qiita.com/Yukimi_Choko/items/c7b978139b254b591242)を参照


## 必要最低限のツール
- HTML: 基本的な構造
- CSS: 見た目の調整
- TypeScript: ロジックと動作
- Node.js（TypeScriptをコンパイルするため）

:::note info
CSSはなくても良いが見た目が整ったり操作が直感的になったりというメリットがあるので、あったほうが良い。CSSは後回しにする事もできるので基本機能が完成してから調整でも良い
:::

## 作業再開する時の覚えときたいメモ
- 履歴が復元され作業をする時、下記を実行する。自分の作業するプロパティを参照して下さい
```typescript
cd my-typescript-project
```

- サーバーを立ち上げる（index.htmlがあるフロントエンドの場合は下記）
```typescript
npx lite-server
```

- TypeScriptを編集したら下記でコンパイルする
```typescript
npx tsc
```
- Open with Live Serverで起動するのは`index.html`






# HTML編集
```typescript
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Task Manager</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Task Manager</h1>
    <input id="task-input" type="text" placeholder="Add a new task">
    <button id="add-task-btn">Add Task</button>
    <ul id="task-list"></ul>
  </div>
  <script src="dist/index.js"></script>
</body>
</html>
```

## HTML解説
- **`<!DOCTYPE html>`**
HTML5ドキュメントであることを宣言している
- **`<html lang="ja">`**
HTMLドキュメントの開始タグ。`lang="ja"`はこのページの言語が日本語である事を指す
- **`<head> セクション`**
ページ設定（表示には直接関係しない情報）
- **`<meta charset="UTF-8">`**
ページの文字エンコーディングをUTF-8に設定。日本語などの多言語対応に必要
- **`<meta name="viewport" content="width=device-width, initial-scale=1.0">`**
ページがレスポンシブ対応（デバイスの画面幅に合わせた表示）するための設定
- **`<title>Task Manager</title>`**
ブラウザのタブに表示されるタイトル
- **`<link rel="stylesheet" href="style.css">`**
外部CSSファイル（style.css）を読み込むためのリンク
- **`<body> セクション`**
ページに表示されるコンテンツ
- **`<div class="container">`**
ページ全体のコンテンツを囲むための親要素。CSSでスタイルを指定しやすくするためにクラス名`container`を付けている
- **`<h1>Task Manager</h1>`**
ページの見出し
- **`<input id="task-input" type="text" placeholder="Add a new task">`**
新しいタスクを入力するためのテキスト入力フィールド。`placeholder="Add a new task"`により、入力欄に説明文が表示される（入力時に消える）
- **`<button id="add-task-btn">Add Task</button>`**
タスクを追加するためのボタン。JavaScriptでボタンの動作を制御
- **`<ul id="task-list"></ul>`**
タスクのリストを表示するための空リスト要素、JavaScriptでリストアイテムを動的に追加
- **`<script src="dist/index.js"></script>`**
外部JavaScriptファイル`dist/index.js`を読み込む。このファイルに、タスクの追加や削除、リストの更新などのロジックが記述されている

### HTMLの役割
- ページの構造を定義（見出し、入力欄、ボタン、リスト）
- 外部CSSをリンクしてデザインを適用
- 外部JavaScriptをリンクして、インタラクティブな動作（タスクの追加など）を実現

### HTMLをブラウザで表示した時
- 上部に「Task Manager」というタイトルが表示
- 入力欄に「Add a new task」というプレースホルダーが表示
- 「Add Task」ボタンが表示
- リストエリアは最初空だがJavaScriptによってタスクが追加されるとアイテムが表示


# CSS編集
フォルダ、ファイルがなかったら作成しておく
- `css/`フォルダを`my-typescript-project`の下に作成
- `css/`フォルダの中に`style.css`ファイルを作成

```typescript
body {
  font-family: Arial, sans-serif;
  background-color: #f0f0f0;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.container {
  background: white;
  padding: 20px;
  border-radius: 5px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  width: 300px;
}

h1 {
  text-align: center;
}

input {
  width: calc(100% - 20px);
  padding: 10px;
  margin-bottom: 10px;
}

button {
  width: 100%;
  padding: 10px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}

ul {
  list-style: none;
  padding: 0;
}

li {
  display: flex;
  justify-content: space-between;
  padding: 5px 0;
  border-bottom: 1px solid #ddd;
}

.completed {
  text-decoration: line-through;
  color: gray;
}
```

## CSS解説

### body（ページ全体のデザイン）

- **`font-family: Arial, sans-serif;`**
ページ全体のフォントをArialに設定。Arialが使えない場合、sans-serifフォントを使用
- **`background-color: #f0f0f0;`**
ページ全体の背景色を薄い灰色（#f0f0f0）に設定
- **`margin: 0; padding: 0;`**
ページ全体の余白と内側の余白をゼロにリセット
- **`display: flex;`**
フレックスボックスレイアウトを使用
※アイテムを行または列に並べるための 1 次元のレイアウト方法
- **`justify-content: center; align-items: center;`**
コンテンツを画面の中央に配置
- **`height: 100vh;`**
ページの高さをビュー全体の高さ（100%）に設定

### .container（コンテンツの幅を調整）
- **`background: white;`**
コンテナの背景色を白に設定
- **`padding: 20px;`**
コンテナ内の余白を20pxに設定
- **`border-radius: 5px;`**
角を丸める（半径5px）
- **`box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);`**
コンテナに薄い影をつけ、立体感を演出
- **`width: 300px;`**
コンテナの幅を300pxに固定

### h1（ページの見出し）
- **`text-align: center;`**
見出し`h1`を中央揃え

### input（入力フォームの見た目）
- **`width: calc(100% - 20px);`**
入力欄の幅を親要素の幅から20px差し引いたサイズに設定（左右に10pxの余白）
- **`padding: 10px;`**
入力欄内の余白を10pxに設定
- **`margin-bottom: 10px;`**
入力欄の下に10pxの余白を設定

### button（ボタンの見た目）
- **`width: 100%;`**
ボタンの幅を親要素に合わせる
- **`padding: 10px;`**
ボタン内の余白を10pxに設定
- **`background-color: #007bff;`**
ボタンの背景色を青色に設定
- **`color: white;`**
ボタン上のテキストを白色に設定
- **`border: none;`**
ボタンの枠線を非表示
- **`border-radius: 5px;`**
ボタンの角を丸める（半径5px）
- **`cursor: pointer;`**
ボタン上にカーソルを載せると、クリック可能な「手」の形に変わる
- **`button:hover`**
ホバー（カーソルを載せた状態）時に背景色を濃い青（#0056b3）に変更


### ul（リストの見た目）
- **`display: flex;`**
各リスト項目をフレックスボックスとしてレイアウト
- **`justify-content: space-between;`**
リスト項目内の内容を左右に配置（例えば、タスク名と削除ボタン）
- **`padding: 5px 0;`**
各リスト項目の上下に5pxの余白を設定
- **`border-bottom: 1px solid #ddd;`**
リスト項目の下に薄いグレーの線を追加


### .completed（CSSで「完了」状態を示すためのクラス）
- **`text-decoration: line-through;`**
テキストを取り消し線で装飾
- **`color: gray;`**
テキストの色を灰色に設定（完了したタスクを視覚的に区別）


### CSSの役割
- ページ全体のデザインを調整し、モダンで見やすいスタイルを適用
- タスク管理アプリの各要素（コンテナ、ボタン、入力欄、リスト）を整える
- ユーザーが使いやすく、操作しやすい見た目とインタラクションを提供


### CSSをブラウザで表示した時
- ページ全体の背景は薄い灰色`#f0f0f0`
- タスク管理ツールのコンテンツは画面中央に表示
- コンテンツ部分`.container`は白背景で、柔らかい影`box-shadow`がつき、立体感が出る
- 全体的にシンプルなデザインで、親しみやすい雰囲気


# TypeScript編集
index.tsにて編集
```typescript
const taskInput = document.getElementById("task-input") as HTMLInputElement;
const addTaskBtn = document.getElementById("add-task-btn") as HTMLButtonElement;
const taskList = document.getElementById("task-list") as HTMLUListElement;

interface Task {
  id: number;
  title: string;
  completed: boolean;
}

let tasks: Task[] = [];

// タスクを追加
addTaskBtn.addEventListener("click", () => {
  const title = taskInput.value.trim();
  if (title === "") return;

  const newTask: Task = {
    id: Date.now(),
    title,
    completed: false,
  };

  tasks.push(newTask);
  renderTasks();
  taskInput.value = ""; // 入力フィールドをクリア
});

// タスクを描画
function renderTasks() {
  taskList.innerHTML = ""; // 現在のリストをクリア
  tasks.forEach((task) => {
    const li = document.createElement("li");
    li.innerHTML = `
      <span class="${task.completed ? "completed" : ""}" data-id="${task.id}">
        ${task.title}
      </span>
      <button data-id="${task.id}">Delete</button>
    `;

    // タスクの状態を切り替え
    li.querySelector("span")?.addEventListener("click", () => toggleTask(task.id));

    // タスクを削除
    li.querySelector("button")?.addEventListener("click", () => deleteTask(task.id));

    taskList.appendChild(li);
  });
}

// タスクの完了状態を切り替え
function toggleTask(id: number) {
  tasks = tasks.map((task) =>
    task.id === id ? { ...task, completed: !task.completed } : task
  );
  renderTasks();
}

// タスクを削除
function deleteTask(id: number) {
  tasks = tasks.filter((task) => task.id !== id);
  renderTasks();
}
```

## TypeScript解説

### 初期設定部分
- **`const taskInput = document.getElementById("task-input") as HTMLInputElement;`**
HTML内の要素`<input id="task-input">`を取得
`as HTMLInputElement`TypeScriptの型アサーション。取得した要素が`HTMLInputElement`であることを明示
- **`const addTaskBtn = document.getElementById("add-task-btn") as HTMLButtonElement;`**
HTML内のボタン要素`<button id="add-task-btn">`を取得
`as HTMLButtonElement`: 型アサーション。取得した要素が`HTMLButtonElement`であることを明示
- **`const taskList = document.getElementById("task-list") as HTMLUListElement;`**
HTML内のリスト要素`<ul id="task-list">`を取得
`as HTMLUListElement`: 型アサーション。取得した要素が`HTMLUListElement`であることを明示

:::note info
`TypeScript`の`const`とは**変数の値を再代入できないようにするためのキーワード**。JavaScriptと同様に、定数を定義する際に使用。ただ`const`が「完全に変更不可」という意味ではなく、「変数そのものの再代入が禁止される」という性質を持つことに注意！
:::

:::note info
**TypeScriptの型アサーション**
コンパイラに対して「この値は特定の型である」と明示的に伝えるための機能。型推論が期待通りに働かない場合や、TypeScriptにデータ構造が安全であることを保証する場面で使われる。型アサーションは、TypeScriptの型チェックを一時的にバイパスするものの、実行時の動作には影響を与えない。
:::



```typescript
interface Task {
  id: number;
  title: string;
  completed: boolean;
}
```
タスクを表現するデータ型（インターフェース）を定義
`id`: タスクの一意の識別子（数値）
`title`: タスクのタイトル（文字列）
`completed`: タスクの完了状態（`true`または`false`）

- **`let tasks: Task[] = [];`**
タスクリスト（`Task`型の配列）を初期化

### タスク追加の処理
- **`addTaskBtn.addEventListener("click", () => {`**
ボタンがクリックされたときに特定の処理（コールバック関数）を実行
- **`const title = taskInput.value.trim();`**
入力欄の値を取得し、前後の不要な空白を削除
`taskInput.value`: 入力欄の現在の値
- **`if (title === "") return;`**
入力欄が空の場合、処理を中断
```typescript
  const newTask: Task = {
    id: Date.now(),
    title,
    completed: false,
  };
```
`newTask`の作成
`id`: 現在のタイムスタンプを利用して一意のIDを生成
`title`: 入力されたタスクタイトル
`completed`: 初期状態では未完了（`false`）
- **`tasks.push(newTask);`**
新しいタスクを`tasks`配列に追加
- **`renderTasks();`**
タスク一覧を再描画する関数を呼び出す
- **`taskInput.value = ""; // 入力フィールドをクリア`**
タスク追加後、入力欄を空にする


### タスク描画の処理
- **`function renderTasks() {`**
タスク一覧を描画する関数を定義
- **`taskList.innerHTML = "";`**
現在のタスクリストをクリア
- **`tasks.forEach((task) => {`**
各タスクをループで処理し、HTML要素を生成
- **`const li = document.createElement("li");`**
新しいリストアイテム`<li>`を生成
```typescript
    li.innerHTML = `
      <span class="${task.completed ? "completed" : ""}" data-id="${task.id}">
        ${task.title}
      </span>
      <button data-id="${task.id}">Delete</button>
    `;
```
タスクのHTML構造を設定
`<span>`: タスクのタイトルを表示
`class="${task.completed ? "completed" : ""}":`タスクが完了済みの場合、CSSクラス`completed`を適用
`<button>`: タスクを削除するボタン
- **`li.querySelector("span")?.addEventListener("click", () => toggleTask(task.id));`**
タスクタイトルをクリックすると、完了状態を切り替える処理を実行
- **`li.querySelector("button")?.addEventListener("click", () => deleteTask(task.id));`**
削除ボタンをクリックすると、該当のタスクを削除
```typescript
    taskList.appendChild(li);
  });
```
作成したリストアイテムをタスクリスト`<ul>`に追加

### タスク完了状態の切り替え
- **`function toggleTask(id: number) {`**
指定されたIDのタスクの完了状態を切り替える関数
```typescript
  tasks = tasks.map((task) =>
    task.id === id ? { ...task, completed: !task.completed } : task
  );
```
タスクの配列を走査し、該当するタスクの`completed`プロパティを反転（`true ↔ false`）
`{ ...task, completed: !task.completed }`:
スプレッド構文を使い、既存のタスクのプロパティを引き継ぎつつ、`completed`のみ変更
- **`renderTasks();`**
完了状態が更新されたタスクリストを再描画

### タスク削除の処理
- **`function deleteTask(id: number) {`**
指定されたIDのタスクを削除する関数
- **`tasks = tasks.filter((task) => task.id !== id);`**
配列`tasks`を走査し、指定されたID以外のタスクを残した新しい配列を生成
- **`renderTasks();`**
削除後のタスクリストを再描画

### TypeScriptの役割
- シンプルなタスク管理アプリのロジックを実装
- 型安全にタスクの追加、表示、完了状態の切り替え、削除の機能を実現


### TypeScriptをブラウザで表示した時
-　TypeScriptで、タスクリストを動的に生成・更新する処理を実現
-　ボタンやクリックイベントでタスクの追加、完了状態の切り替え、削除を実行可能


# 完成品
![タスク管理ツール](https://d.kuku.lu/yzn27cfyd)









## **個人メモ**
- ファイル名
`C:\Users\ユーザー名\Visual Studio Code\my-typescript-project`
- 外部JavaScriptファイルのパス
`<script src="dist/index.js"></script>`

- 元のHTML
```typescript
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TypeScript Project</title>
</head>
<body>
    <script src="dist/index.js"></script>
</body>
</html>
```

- 元のindex.ts
```typescript
// 挨拶を生成する関数
const greet = (name: string): string => {
    return `Hello, ${name}!`;
  };
  
  // コンソールに出力
  console.log(greet("World"));
  ```

**★更新★**
2025/01/03　初めての個人開発着手
2025/01/05  完成（改善の余地あり）