# 機能設計書 38-Teleport

## 概要

本ドキュメントは、Vue 3の組み込みコンポーネント`<Teleport>`の機能設計を記載する。`<Teleport>`はコンポーネントの子要素を、コンポーネントのDOM階層とは異なる任意のDOM位置にレンダリングするためのコンポーネントである。

### 本機能の処理概要

`<Teleport>`コンポーネントは、論理的にはコンポーネントツリー内に属しつつも、DOM上では指定されたターゲット要素の下にレンダリングする「ポータル」機能を提供する。これにより、モーダルダイアログ、ツールチップ、通知などのUI要素を、CSSのz-indexやoverflow問題を回避して適切なDOM位置に配置できる。

**業務上の目的・背景**：モーダルダイアログやポップオーバーなどのオーバーレイUIは、コンポーネントのDOM階層内にレンダリングされると、親要素のCSS（overflow: hidden, z-index, transform等）の影響を受けて正しく表示されない問題がある。Teleportはこの問題を解決し、UIを`<body>`直下等の適切な位置にレンダリングしつつ、Vueのコンポーネントツリー内でのデータフローとイベント伝播を維持する。

**機能の利用シーン**：モーダルダイアログ、ドロップダウンメニュー、ツールチップ、通知トースト、全画面オーバーレイ等。

**主要な処理内容**：
1. `to`プロパティで指定されたターゲット要素の解決（`resolveTarget`）
2. 初回マウント時の子要素のターゲットへのレンダリング
3. `disabled`プロパティによるインプレースレンダリング切替
4. `defer`プロパティによるマウント遅延制御
5. ターゲット変更時の子要素の移動（`moveTeleport`）
6. disabled/enabled切替時の子要素の移動
7. SSRハイドレーション対応（`hydrateTeleport`）
8. CSS変数の更新（`updateCssVars`）
9. SVG/MathMLネームスペースの自動検出

**関連システム・外部連携**：レンダラー（`renderer.ts`）のprocess/move/removeメソッドと連携。Suspenseとの連携。カスタム要素（Custom Element）のテレポートターゲット追跡。

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

## 関連画面

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

## 機能種別

DOM配置制御 / 組み込みコンポーネント

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| to | `string \| RendererElement \| null \| undefined` | Yes（disabled=false時） | テレポート先のターゲット要素またはCSSセレクタ | 文字列の場合querySelectorで解決 |
| disabled | `boolean` | No | trueの場合、テレポートせずインプレースでレンダリング | - |
| defer | `boolean` | No | trueの場合、マウントをqueuePostRenderEffectで遅延 | - |

### 入力データソース

- テンプレート内の`<Teleport>`コンポーネントのprops
- デフォルトスロットの子要素

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| placeholder | `Text/Comment` | メインDOMツリー内のプレースホルダーノード |
| targetStart/targetAnchor | `Text` | ターゲット要素内のアンカーノード |
| children | `VNode[]` | テレポートされた子要素 |

### 出力先

- `to`で指定されたターゲットDOM要素内
- `disabled`時はインプレース（元のDOM位置）

## 処理フロー

### 処理シーケンス

```
初回マウント:
1. プレースホルダー（el）とアンカー（anchor）をメインコンテナに挿入
2. mountToTarget:
   ├─ resolveTarget()でターゲット要素を解決
   ├─ prepareAnchor()でtargetStart/targetAnchorを作成・挿入
   ├─ SVG/MathML名前空間の検出
   └─ disabled=falseならターゲットに子要素をマウント
3. disabled=trueならメインコンテナに子要素をマウント
4. defer=trueならqueuePostRenderEffectで遅延実行

更新時:
1. 前回のel/anchor/targetを引き継ぎ
2. dynamicChildrenがあればpatchBlockChildren、なければpatchChildren
3. disabled状態の変化に応じてmoveTeleport
4. ターゲットの変更に応じてmoveTeleport
```

### フローチャート

```mermaid
flowchart TD
    A[Teleport process] --> B{初回マウント?}
    B -->|Yes| C[プレースホルダー挿入]
    C --> D{disabled?}
    D -->|Yes| E[メインコンテナにマウント]
    D -->|No| F[resolveTarget]
    F --> G{defer?}
    G -->|Yes| H[queuePostRenderEffectでmountToTarget]
    G -->|No| I[mountToTarget実行]
    B -->|No| J[前回状態引き継ぎ]
    J --> K[patchChildren]
    K --> L{disabled変化?}
    L -->|enabled→disabled| M[メインコンテナに移動]
    L -->|disabled→enabled| N[ターゲットに移動]
    L -->|ターゲット変更| O[新ターゲットに移動]
    L -->|変化なし| P[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | ターゲット解決 | toが文字列の場合querySelectorで解決、要素の場合はそのまま使用 | 常時 |
| BR-38-02 | ターゲット存在チェック | ターゲットが見つからない場合、disabled=falseなら開発モードで警告 | 開発モード |
| BR-38-03 | disabled制御 | disabled=trueの場合、子要素はメインコンテナ内にレンダリング | disabled prop |
| BR-38-04 | defer制御 | defer=trueの場合、ターゲットへのマウントを次のレンダリングサイクルに遅延 | defer prop |
| BR-38-05 | SVG/MathML検出 | ターゲット要素がSVG/MathML内の場合、適切な名前空間を設定 | ターゲットの種類による |
| BR-38-06 | TeleportEndKey | targetStartにTeleportEndKeyシンボルでtargetAnchorへの参照を持たせ、nextSibling検索をスキップ可能にする | 常時 |
| BR-38-07 | CE連携 | カスタム要素内のTeleportの場合、ターゲット要素をce._teleportTargetsに追跡 | isCE時 |

### 計算ロジック

`resolveTarget`: `props.to`が文字列なら`querySelector`で検索、オブジェクトならそのまま返却。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発時警告 | querySelectorが利用不可 | `Current renderer does not support string target for Teleports.` |
| - | 開発時警告 | ターゲットが見つからない（disabled=false時） | `Failed to locate Teleport target with selector "${targetSelector}".` |
| - | 開発時警告 | to propが無効（null/undefined, disabled=false時） | `Invalid Teleport target: ${targetSelector}` |
| - | 開発時警告 | 更新時にターゲットが無効 | `Invalid Teleport target on update:` |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- `__isTeleport`マーカーにより、レンダラーはTeleportをインポートせずに検出可能（tree-shaking対応）
- `dynamicChildren`がある場合は`patchBlockChildren`で高速パッチ
- `traverseStaticChildren`で静的ノードのDOM参照を維持

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

- `to`プロパティにユーザー入力を直接渡す場合、XSSリスクに注意（querySelectorに任意の文字列が渡される可能性）

## 備考

- Teleportは常に配列の子要素を持つ（コンパイラとVNode正規化により保証）
- `TeleportMoveTypes`列挙型: `TARGET_CHANGE`（ターゲット変更）、`TOGGLE`（disabled切替）、`REORDER`（メインビュー内の並べ替え）
- `updateCssVars`: CSS変数を使用するコンポーネント（`ctx.ut`メソッド持ち）のために、テレポート先の子要素に`data-v-owner`属性を設定

---

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

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

### 推奨読解順序

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

TeleportPropsとTeleportMoveTypesを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Teleport.ts | `packages/runtime-core/src/components/Teleport.ts` | `TeleportProps`（20-24行目）、`TeleportMoveTypes`（345-349行目）、`TeleportEndKey`（26行目） |

#### Step 2: processメソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Teleport.ts | `packages/runtime-core/src/components/Teleport.ts` | `TeleportImpl.process()`（78-300行目） |

**主要処理フロー**:
1. **107-180行目**: 初回マウント処理 - プレースホルダー挿入、ターゲット解決、子要素マウント
2. **181-299行目**: 更新処理 - patchChildren、disabled変化、ターゲット変更の処理
3. **42-73行目**: `resolveTarget`関数 - ターゲット要素の解決

#### Step 3: moveTeleport関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Teleport.ts | `packages/runtime-core/src/components/Teleport.ts` | `moveTeleport`関数（351-388行目） |

#### Step 4: removeとhydrationを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Teleport.ts | `packages/runtime-core/src/components/Teleport.ts` | `remove`（302-339行目）、`hydrateTeleport`（395-499行目） |

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

```
<Teleport>
    │
    ├─ TeleportImpl.process(n1, n2, ...)
    │      ├─ 初回マウント（n1 == null）
    │      │      ├─ insert(placeholder, container)
    │      │      ├─ insert(mainAnchor, container)
    │      │      ├─ mountToTarget()
    │      │      │      ├─ resolveTarget(props, querySelector)
    │      │      │      ├─ prepareAnchor(target, vnode, ...)
    │      │      │      └─ mountChildren(children, target, ...)
    │      │      └─ updateCssVars(n2, disabled)
    │      └─ 更新（n1 != null）
    │             ├─ patchBlockChildren / patchChildren
    │             └─ moveTeleport(n2, container/target, ...)
    │
    ├─ TeleportImpl.remove(vnode, ...)
    │      └─ unmount(children)
    │
    ├─ moveTeleport(vnode, container, parentAnchor, ...)
    │      ├─ insert(targetAnchor) for TARGET_CHANGE
    │      ├─ insert(el) for REORDER
    │      └─ move(children[i]) if !isReorder || disabled
    │
    └─ hydrateTeleport(node, vnode, ...)
           ├─ resolveTarget()
           └─ hydrateChildren()
```

### データフロー図

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

to="body"             ┌──────────────────────────┐
disabled=false ───────▶│ TeleportImpl.process      │
                    │  ├ resolveTarget("body")   │
                    │  │  └ querySelector("body")│──────▶ <body>にマウント
                    │  ├ prepareAnchor           │──────▶ targetStart/targetAnchor
                    │  ├ mountChildren            │──────▶ 子要素をターゲットに
                    │  └ placeholder              │──────▶ 元の位置にプレースホルダー
                    └──────────────────────────┘

disabled=true ────────▶ 子要素は元の位置にインプレースレンダリング
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Teleport.ts | `packages/runtime-core/src/components/Teleport.ts` | ソース | Teleportコンポーネントのメイン実装 |
| renderer.ts | `packages/runtime-core/src/renderer.ts` | ソース | process/move/removeの呼び出し元 |
| hydration.ts | `packages/runtime-core/src/hydration.ts` | ソース | SSRハイドレーション連携 |
| vnode.ts | `packages/runtime-core/src/vnode.ts` | ソース | VNode定義（targetStart, targetAnchor等） |
| index.ts | `packages/runtime-core/src/index.ts` | ソース | Teleportのエクスポート |
| Teleport.spec.ts | `packages/runtime-core/__tests__/components/Teleport.spec.ts` | テスト | Teleportのテスト |
