# 機能設計書 34-v-on修飾子

## 概要

本ドキュメントは、Vue 3の`v-on`ディレクティブにおけるイベント修飾子のランタイム実装の機能設計を記載する。`withModifiers()`関数と`withKeys()`関数により、イベントハンドラに`.stop`、`.prevent`、`.ctrl`、`.enter`等の修飾子機能を付与する。

### 本機能の処理概要

`v-on`修飾子は、テンプレート内のイベントハンドラに対して、イベントの伝播制御、デフォルト動作の抑制、キー修飾、マウスボタン修飾等の機能を宣言的に付与する仕組みである。テンプレートコンパイラがv-onディレクティブの修飾子を解析し、`withModifiers()`または`withKeys()`のランタイムヘルパー呼び出しに変換する。

**業務上の目的・背景**：イベントハンドラ内で`event.stopPropagation()`や`event.preventDefault()`を毎回手動で呼ぶのは冗長であり、テンプレート内で宣言的に修飾子を指定できることにより、開発者の生産性とコードの可読性が向上する。

**機能の利用シーン**：フォーム送信時のデフォルト動作抑制（`.prevent`）、イベントバブリングの停止（`.stop`）、特定キー入力時のみの処理（`.enter`、`.esc`等）、Ctrl+Sのショートカット無効化（`.ctrl.s.prevent`）、右クリックメニューの制御（`.right`）等。

**主要な処理内容**：
1. `withModifiers()`: イベント修飾子（stop, prevent, self, ctrl, shift, alt, meta, left, middle, right, exact）を適用
2. `withKeys()`: キー修飾子（特定キー名によるフィルタリング）を適用
3. キャッシュ機構による同一修飾子組み合わせの再利用
4. Vue 2互換のキーエイリアス（esc→escape, space→' '等）サポート

**関連システム・外部連携**：`@vue/compiler-dom`のv-on変換処理と連携。コンパイル時に修飾子が解析され、適切なランタイムヘルパー呼び出しに変換される。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | App（メイン画面） | 補助機能 | @keydown.ctrl.s.prevent / @keydown.meta.s.preventによるブラウザ保存ショートカットの無効化 |
| 3 | VersionSelect（バージョン選択） | 補助機能 | @click.stop修飾子によるイベント伝播制御（ドロップダウン内クリック時） |

## 機能種別

イベント制御 / ディレクティブヘルパー

## 入力仕様

### 入力パラメータ

#### withModifiers()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fn | `(event: Event, ...args: unknown[]) => any` | Yes | 元のイベントハンドラ関数 | - |
| modifiers | `VOnModifiers[]` | Yes | 適用する修飾子の配列 | - |

#### withKeys()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fn | `(event: KeyboardEvent) => any` | Yes | 元のキーボードイベントハンドラ関数 | - |
| modifiers | `string[]` | Yes | フィルタリングするキー名の配列 | - |

### 入力データソース

- テンプレートコンパイラが生成するレンダリング関数内のイベントハンドラ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| wrappedHandler | `T` | 修飾子が適用されたラップ済みイベントハンドラ |

### 出力先

- VNodeのイベントハンドラプロパティ

## 処理フロー

### 処理シーケンス

```
withModifiers():
1. キャッシュの確認（fn._withMods[cacheKey]）
   └─ 存在すればキャッシュを返却
2. ラッパー関数の生成
   ├─ 各修飾子のガード関数を順に実行
   ├─ ガードがfalsy値を返した（=条件不一致の）場合はreturn
   └─ すべてのガードを通過した場合のみ元のfnを実行
3. キャッシュに保存して返却

withKeys():
1. キャッシュの確認（fn._withKeys[cacheKey]）
   └─ 存在すればキャッシュを返却
2. ラッパー関数の生成
   ├─ event.keyが存在しなければreturn
   ├─ event.keyをハイフネーションして比較
   ├─ keyNamesエイリアスも含めてマッチング
   └─ マッチした場合のみ元のfnを実行
3. キャッシュに保存して返却
```

### フローチャート

```mermaid
flowchart TD
    A[withModifiers呼び出し] --> B{キャッシュあり?}
    B -->|Yes| C[キャッシュ返却]
    B -->|No| D[ラッパー関数生成]
    D --> E[イベント発火時]
    E --> F{各修飾子ガードを順に実行}
    F -->|ガード不通過| G[処理中断]
    F -->|全ガード通過| H[元のfn実行]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | stopガード | `e.stopPropagation()`を呼び出す | `.stop`修飾子 |
| BR-34-02 | preventガード | `e.preventDefault()`を呼び出す | `.prevent`修飾子 |
| BR-34-03 | selfガード | `e.target !== e.currentTarget`の場合はスキップ | `.self`修飾子 |
| BR-34-04 | システムキーガード | 対応するキーが押されていない場合はスキップ | `.ctrl`/`.shift`/`.alt`/`.meta`修飾子 |
| BR-34-05 | マウスボタンガード | 指定ボタン以外の場合はスキップ（left=0, middle=1, right=2） | `.left`/`.middle`/`.right`修飾子 |
| BR-34-06 | exactガード | 指定されていないシステムキーが押されている場合はスキップ | `.exact`修飾子 |
| BR-34-07 | キーエイリアス | esc→escape, space→' ', up→arrow-up, left→arrow-left, right→arrow-right, down→arrow-down, delete→backspace | withKeys内のkeyNames |
| BR-34-08 | キャッシュ | `modifiers.join('.')`をキーとしてラッパー関数をキャッシュ | 常時 |

### 計算ロジック

修飾子ガードの実行順序: 配列の先頭から順にguardを実行し、いずれかがtruthyを返した時点で処理を中断する。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 互換性警告 | withKeysで数値キーコード修飾子が使用された場合（Vue 2互換モード） | `V_ON_KEYCODE_MODIFIER`非推奨警告 |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- `_withMods`/`_withKeys`キャッシュにより、同一修飾子組み合わせのラッパー関数は一度だけ生成される
- 修飾子ガードは軽量な条件チェックのみで、イベント処理のオーバーヘッドは最小限

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

特記事項なし。

## 備考

- `selfガード`は他のガードと異なり、条件不一致時に`true`を返す（`e.target !== e.currentTarget`の場合にスキップ）
- `exactガード`は指定されていないシステムキーが押されているかチェックし、純粋な修飾子の組み合わせを保証する
- Vue 2互換モード（`__COMPAT__`）では`config.keyCodes`によるカスタムキーコードマッピングもサポート

---

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

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

### 推奨読解順序

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

修飾子の型定義と修飾子ガードのマッピングを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | vOn.ts | `packages/runtime-dom/src/directives/vOn.ts` | `VOnModifiers`型（15行目）、`ModifierGuards`型（18-29行目）、`modifierGuards`定数（30-47行目） |

**読解のコツ**: `modifierGuards`はRecord型で、各修飾子名に対応するガード関数を定義する。ガード関数がtruthy値を返すとイベントハンドラの実行がスキップされる。`stop`と`prevent`はvoid（副作用のみ）、その他はboolean値を返す。

#### Step 2: withModifiers()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vOn.ts | `packages/runtime-dom/src/directives/vOn.ts` | `withModifiers()`関数（52-70行目） |

**主要処理フロー**:
1. **58行目**: `fn._withMods`キャッシュの取得または初期化
2. **59行目**: `modifiers.join('.')`でキャッシュキー生成
3. **62-68行目**: ラッパー関数の生成 - 各修飾子のguardを順に実行

#### Step 3: withKeys()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vOn.ts | `packages/runtime-dom/src/directives/vOn.ts` | `withKeys()`関数（90-161行目）、`keyNames`定数（74-85行目） |

**主要処理フロー**:
1. **113-114行目**: キャッシュ取得・初期化
2. **119行目**: `event.key`の存在チェック
3. **123行目**: `hyphenate(event.key)`でキー名をハイフネーション
4. **125-131行目**: 修飾子とキー名のマッチング（エイリアス含む）
5. **134-158行目**: Vue 2互換モードでのキーコードサポート

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

```
テンプレートコンパイラ（v-on変換）
    │
    ├─ withModifiers(fn, modifiers)
    │      └─ modifierGuards[modifier](event, modifiers)
    │            ├─ stop: e.stopPropagation()
    │            ├─ prevent: e.preventDefault()
    │            ├─ self: e.target !== e.currentTarget
    │            ├─ ctrl/shift/alt/meta: !(e as KeyedEvent).{key}Key
    │            ├─ left/middle/right: e.button !== {0,1,2}
    │            └─ exact: systemModifiers.some(...)
    │
    └─ withKeys(fn, modifiers)
           ├─ hyphenate(event.key)
           ├─ keyNames[modifier] マッチング
           └─ (compat) globalKeyCodes マッチング
```

### データフロー図

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

テンプレート修飾子    ┌──────────────────────────┐
  .stop.prevent ─────▶│ コンパイラ                │
                    │  └ withModifiers(fn,[...]) │
                    └──────────┬───────────────┘
                               │
DOMイベント            ┌───────▼──────────────────┐
  (click/keydown) ───▶│ ラッパー関数               │
                    │  ├ guard(stop) → stopProp  │
                    │  ├ guard(prevent) → prevDef│
                    │  └ fn(event)              │──────▶ ハンドラ実行
                    └──────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vOn.ts | `packages/runtime-dom/src/directives/vOn.ts` | ソース | withModifiers/withKeysのメイン実装 |
| index.ts | `packages/runtime-dom/src/index.ts` | ソース | withModifiers/withKeysのエクスポート |
| runtimeHelpers.ts | `packages/compiler-dom/src/runtimeHelpers.ts` | ソース | コンパイラからのランタイムヘルパー参照 |
| transformElement.ts | `packages/compiler-core/src/transforms/transformElement.ts` | ソース | v-onディレクティブの変換処理 |
| vOn.spec.ts | `packages/runtime-dom/__tests__/directives/vOn.spec.ts` | テスト | withModifiers/withKeysのテスト |
