# 機能設計書 36-TransitionGroup

## 概要

本ドキュメントは、Vue 3の組み込みコンポーネント`<TransitionGroup>`の機能設計を記載する。`<TransitionGroup>`はリスト要素のグループに対してenter/leave/moveアニメーションを管理するコンポーネントである。

### 本機能の処理概要

`<TransitionGroup>`コンポーネントは、リスト内の各要素に対してenter/leaveトランジションを適用するとともに、FLIP(First, Last, Invert, Play)技術を用いて要素の移動時にもスムーズなアニメーションを提供する。`<Transition>`と異なり、複数の子要素を扱い、`mode`プロパティはサポートしない。

**業務上の目的・背景**：リスト表示はWebアプリケーションの基本的なUIパターンであり、アイテムの追加・削除・並べ替え時にアニメーションを付けることで、ユーザーにとって変化の把握が容易になる。FLIP技術により、CSSだけでは困難な要素の移動アニメーションを自動的に実現する。

**機能の利用シーン**：ToDoリストのアイテム追加/削除/並べ替え、フィルタリングによるリスト変更、ソート変更時のアニメーション、チャットメッセージの表示等。

**主要な処理内容**：
1. `setup()`で`useTransitionState()`によるトランジション状態管理
2. `onUpdated()`フックでのFLIP移動アニメーション処理
3. レンダリング関数で各子要素に`resolveTransitionHooks`と`setTransitionHooks`を適用
4. `positionMap`/`newPositionMap`による要素位置の追跡
5. `hasCSSTransform`によるmoveクラスのCSS変換サポートチェック
6. 子要素への`key`プロパティの必須チェック

**関連システム・外部連携**：`Transition`コンポーネントの`resolveTransitionProps`と各種ユーティリティ関数を再利用。`BaseTransition`の`useTransitionState`、`resolveTransitionHooks`、`setTransitionHooks`と連携。

**権限による制御**：特になし。

## 関連画面

画面機能マッピングに該当する画面関連情報なし。

## 機能種別

アニメーション制御 / 組み込みコンポーネント

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tag | `string` | No | ラッパー要素のタグ名（デフォルト: Fragment） | - |
| moveClass | `string` | No | 移動時のCSSクラス（デフォルト: `{name}-move`） | - |
| name | `string` | No | CSSクラスのプレフィックス（Transitionから継承） | - |
| *(Transitionの全props)* | - | No | modeを除くTransitionのすべてのprops | modeは削除される |

### 入力データソース

- テンプレート内の`<TransitionGroup>`コンポーネントのprops
- デフォルトスロットの子要素（各要素にkeyが必要）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| VNode | `VNode` | tagでラップされた子要素のVNode |
| CSS class/transform操作 | - | 移動アニメーション用のtransform/transitionDuration操作 |

### 出力先

- DOM要素のclassList, style.transform, style.transitionDuration

## 処理フロー

### 処理シーケンス

```
レンダリングフェーズ:
1. 前回の子要素(children)をprevChildrenに保存
2. 各prevChildにsetTransitionHooks + positionMap記録
3. 新しい子要素を取得し、各子にsetTransitionHooks
4. createVNode(tag, null, children) を返却

onUpdatedフェーズ（FLIP処理）:
1. prevChildrenが空なら終了
2. hasCSSTransformでmoveクラスの有効性チェック
3. callPendingCbs: 未完了のenter/moveコールバックを実行
4. recordPosition: 各要素の新しい位置を記録
5. applyTranslation: 旧位置→新位置の差分をtransformで設定
6. forceReflow: 強制リフロー
7. movedChildren: 移動があった要素にmoveクラスを追加、transform/transitionDurationをリセット
8. transitionendイベントで moveクラスを削除
```

### フローチャート

```mermaid
flowchart TD
    A[onUpdated] --> B{prevChildren空?}
    B -->|Yes| C[終了]
    B -->|No| D[hasCSSTransform確認]
    D -->|No| E[prevChildren空にして終了]
    D -->|Yes| F[callPendingCbs]
    F --> G[recordPosition]
    G --> H[applyTranslation]
    H --> I[forceReflow]
    I --> J[movedChildrenにmoveClass追加]
    J --> K[transform/transitionDurationリセット]
    K --> L[transitionendで moveClass削除]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-01 | key必須 | TransitionGroupの子要素にはkeyプロパティが必須 | 常時（開発モードで警告） |
| BR-36-02 | mode非サポート | TransitionGroupではmodeプロパティは使用不可（propsから削除） | 常時 |
| BR-36-03 | FLIP技術 | 旧位置と新位置の差分をCSS transformで逆方向に設定し、アニメーションで元に戻す | 要素の位置が変化した場合 |
| BR-36-04 | 3ループ分離 | DOM読み取りと書き込みを別ループで行い、layout thrashingを防止 | onUpdated時 |
| BR-36-05 | moveClass検出 | クローン要素にmoveClassを適用してhasTransformを確認し、CSSにトランジションが定義されているかチェック | onUpdated時 |
| BR-36-06 | デフォルトtag | Vue 3ではデフォルトがFragment、Vue 2互換モードではspan | 常時 |

### 計算ロジック

`applyTranslation`: `dx = oldPos.left - newPos.left`, `dy = oldPos.top - newPos.top`。差分がある場合に`translate(${dx}px,${dy}px)`を設定。

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発時警告 | 子要素にkeyが設定されていない | `<TransitionGroup> children must be keyed.` |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- DOM読み取り（offsetLeft/offsetTop）と書き込み（style.transform）を別ループで実行し、layout thrashingを回避
- `WeakMap`で位置情報を管理し、メモリリークを防止
- `hasCSSTransform`チェックにより、CSSが定義されていない場合はFLIP処理をスキップ

## セキュリティ考慮事項

特記事項なし。

## 備考

- `positionMap`と`newPositionMap`はWeakMapで実装され、VNodeの参照がなくなると自動的にGCされる
- `moveCbKey`と`enterCbKey`はSymbolで定義され、DOM要素に直接コールバックを保持する
- `hasCSSTransform`はクローン要素を一時的にDOMに追加してtransform有無を確認する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

位置情報管理とコールバックキーの定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | `Position`インターフェース（32-35行目）、WeakMap定義（37-38行目）、Symbol定義（39-40行目） |
| 1-2 | TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | `TransitionGroupProps`型（42-45行目） |

#### Step 2: setupとレンダリング関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | `TransitionGroupImpl.setup()`（69-177行目） |

**主要処理フロー**:
1. **70-73行目**: インスタンス取得、状態初期化
2. **75-120行目**: `onUpdated` - FLIP移動アニメーション処理
3. **122-176行目**: レンダリング関数 - prevChildren保存、新children取得、VNode返却

#### Step 3: FLIP処理の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | `callPendingCbs`（186-194行目）、`recordPosition`（196-201行目）、`applyTranslation`（203-214行目） |

#### Step 4: moveクラス検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | `hasCSSTransform`（216-242行目） |

### プログラム呼び出し階層図

```
<TransitionGroup>
    │
    ├─ setup()
    │      ├─ useTransitionState()
    │      ├─ onUpdated()
    │      │      ├─ hasCSSTransform()
    │      │      │      └─ getTransitionInfo() (from Transition.ts)
    │      │      ├─ callPendingCbs()
    │      │      ├─ recordPosition()
    │      │      ├─ applyTranslation()
    │      │      ├─ forceReflow() (from Transition.ts)
    │      │      ├─ addTransitionClass() (from Transition.ts)
    │      │      └─ removeTransitionClass() (from Transition.ts)
    │      │
    │      └─ return () => {...} (レンダリング関数)
    │             ├─ resolveTransitionProps() (from Transition.ts)
    │             ├─ resolveTransitionHooks() (from BaseTransition.ts)
    │             ├─ setTransitionHooks() (from BaseTransition.ts)
    │             ├─ getTransitionRawChildren() (from BaseTransition.ts)
    │             └─ createVNode(tag, null, children)
    │
    └─ props: TransitionPropsValidators + { tag, moveClass }
```

### データフロー図

```
[入力]                     [処理]                              [出力]

スロット子要素        ┌──────────────────────────┐
  (v-forリスト) ─────▶│ レンダリング関数            │
                    │  ├ prevChildren位置記録     │
                    │  ├ 新children hooks設定    │
                    │  └ createVNode(tag,children)│──────▶ VNode
                    └──────────────────────────┘

DOM更新後            ┌──────────────────────────┐
  (onUpdated) ───────▶│ FLIP処理                  │
                    │  ├ 新位置記録(recordPosition)│
                    │  ├ 差分計算(applyTranslation)│
                    │  ├ forceReflow             │
                    │  └ moveClass追加→アニメ開始  │──────▶ el.style.transform
                    │     └ transitionend        │──────▶ moveClass除去
                    └──────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TransitionGroup.ts | `packages/runtime-dom/src/components/TransitionGroup.ts` | ソース | TransitionGroupコンポーネントのメイン実装 |
| Transition.ts | `packages/runtime-dom/src/components/Transition.ts` | ソース | resolveTransitionProps、addTransitionClass等の再利用 |
| BaseTransition.ts | `packages/runtime-core/src/components/BaseTransition.ts` | ソース | useTransitionState、resolveTransitionHooks等 |
| index.ts | `packages/runtime-dom/src/index.ts` | ソース | TransitionGroupのエクスポート |
