# 機能設計書 58-セレクトウィジェット

## 概要

本ドキュメントは、Decap CMSにおけるセレクトウィジェット（Select Widget）の設計を記述する。ドロップダウン選択UIを提供し、単一選択と複数選択の両方に対応するウィジェットである。

### 本機能の処理概要

**業務上の目的・背景**：CMSのコンテンツには、カテゴリ・タグ・ステータスなど事前定義された選択肢から値を選ぶフィールドが多い。本ウィジェットはreact-selectライブラリをベースとしたドロップダウン選択UIを提供する。config.ymlで定義した選択肢一覧から単一または複数の値を選択でき、文字列だけでなく数値も選択肢として利用可能である。

**機能の利用シーン**：エントリ編集画面・エントリ新規作成画面において、`widget: select`が指定されたフィールドに対してドロップダウンセレクトをレンダリングする。`multiple: true`で複数選択モードになる。

**主要な処理内容**：
1. react-selectコンポーネントによるドロップダウンUIのレンダリング
2. 単一選択モード・複数選択モードの切り替え
3. 選択肢の文字列/オブジェクト（{label, value}）形式対応
4. 複数選択時のmin/maxバリデーション
5. 必須フィールドの空値処理
6. プレビューコンポーネントでの単一値/リスト表示

**関連システム・外部連携**：react-select、decap-cms-lib-widgets（validations）、Immutable.js

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | エントリ編集画面 | 主機能 | 選択肢フィールドの値選択 |
| 7 | エントリ新規作成画面 | 主機能 | 選択肢フィールドの値選択 |

## 機能種別

UI入力コンポーネント / バリデーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| onChange | function | Yes | 値変更時のコールバック | - |
| value | node | No | 現在の選択値 | - |
| forID | string | Yes | select要素のid属性 | - |
| classNameWrapper | string | Yes | ラッパーのCSSクラス名 | - |
| setActiveStyle | function | Yes | フォーカス時のスタイル設定関数 | - |
| setInactiveStyle | function | Yes | ブラー時のスタイル設定関数 | - |
| field | ImmutableMap | Yes | フィールド設定（options, multiple, min, max, required等） | options必須 |
| t | function | No | ローカライズ関数 | - |

### フィールド設定（config.yml）

| 設定名 | 型 | 必須 | 説明 |
|--------|-----|-----|------|
| options | array | Yes | 選択肢一覧（文字列 / 数値 / {label, value}オブジェクト） |
| multiple | boolean | No | 複数選択モード |
| min | integer | No | 最小選択数（multipleモード時） |
| max | integer | No | 最大選択数（multipleモード時） |

### 入力データソース

エディタのコントロールペインから提供されるprops。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| value（単一） | string / number / null | 選択された値 |
| value（複数） | ImmutableList | 選択された値のリスト |

### 出力先

onChangeコールバック経由でReduxストアのエントリドラフトに反映される。

## 処理フロー

### 処理シーケンス

```
1. コンポーネントマウント
   └─ PropTypesの手動バリデーション
   └─ required && multiple の場合、value が List でなければ自動変換
2. optionsをconvertToOptionで統一形式に変換
   └─ 文字列 → {label, value}
   └─ ImmutableMap → plain object
3. getSelectedValueで現在の選択値を解決
   └─ 単一: options.find(['value', value])
   └─ 複数: value配列をoptionsからマッチ
4. ユーザーが選択/選択解除
   └─ handleChange → 単一/複数で分岐
5. バリデーション（isValid呼び出し時）
   └─ 複数選択モードのみ: validateMinMaxで選択数チェック
```

### フローチャート

```mermaid
flowchart TD
    A[Select レンダリング] --> B{multiple?}
    B -->|No| C[単一選択モード]
    B -->|Yes| D[複数選択モード]
    C --> E[ユーザー選択]
    D --> E
    E --> F[handleChange]
    F --> G{multiple?}
    G -->|No| H{空選択?}
    H -->|Yes| I["onChange(null)"]
    H -->|No| J["onChange(value)"]
    G -->|Yes| K{空選択?}
    K -->|Yes & required| L["onChange(List())"]
    K -->|Yes & !required| I
    K -->|No| M["onChange(fromJS(values))"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | 選択肢フォーマット | 文字列・数値はそのまま{label, value}に変換。ImmutableMapはtoJS()で変換 | 常時 |
| BR-58-02 | クリア可能条件 | required=falseまたはmultiple=trueの場合、選択をクリア可能 | 常時 |
| BR-58-03 | 複数選択初期値 | required && multipleの場合、value未設定時は空のList（fromJS([])）を設定 | componentDidMount時 |
| BR-58-04 | 複数選択値変換 | required && multipleの場合、valueがListでなければListに変換 | componentDidMount時 |
| BR-58-05 | min/maxバリデーション | 複数選択モードのみ、validateMinMaxで選択数のバリデーションを実行 | isValid呼び出し時 |
| BR-58-06 | 単一選択空値 | 単一選択で空選択時はnullをonChangeに渡す | 選択解除時 |

### 計算ロジック

特になし。

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

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 範囲エラー | 複数選択で選択数がmin/maxの範囲外 | validateMinMaxによるエラーメッセージ返却 |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

特になし。

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

react-selectによるレンダリングのため、ブラウザのデフォルトのセキュリティ制御が適用される。

## 備考

- react-selectのreactSelectStylesでスタイリング
- Immutable.jsのList/fromJSを使用した値の変換が行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.js | `packages/decap-cms-widget-select/src/schema.js` | スキーマ定義: options（配列、文字列/数値/オブジェクト）、multiple, min, max |
| 1-2 | index.js | `packages/decap-cms-widget-select/src/index.js` | Widget関数がname: 'select'とschemaを含む |

#### Step 2: ユーティリティ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SelectControl.js | `packages/decap-cms-widget-select/src/SelectControl.js` | optionToString（10-14行目）、convertToOption（16-21行目）、getSelectedValue（23-38行目）: 選択肢の変換・解決ロジック |

#### Step 3: コントロールコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SelectControl.js | `packages/decap-cms-widget-select/src/SelectControl.js` | isValid（62-80行目）: 複数選択時のmin/maxバリデーション。handleChange（82-97行目）: 単一/複数の分岐処理。componentDidMount（99-111行目）: required && multipleの初期値補正 |

**主要処理フロー**:
- **62-80行目**: isValid - multipleモードのみvalidateMinMaxを呼び出し
- **82-97行目**: handleChange - 空選択/単一/複数の3パターン分岐
- **99-111行目**: componentDidMount - PropTypes検証 + required && multipleの値補正
- **113-142行目**: render - react-selectの各propを設定

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

```
DecapCmsWidgetSelect (index.js)
    |
    +-- Widget()
    |       └── { name: 'select', controlComponent, previewComponent, schema }
    |
    +-- SelectControl (SelectControl.js)
    |       +-- optionToString() - option → value文字列
    |       +-- convertToOption() - raw → {label, value}
    |       +-- getSelectedValue() - 現在値の解決
    |       +-- isValid() - min/maxバリデーション
    |       |       └── validations.validateMinMax (decap-cms-lib-widgets)
    |       +-- handleChange() - 選択変更処理
    |       +-- componentDidMount() - 初期値補正
    |       └── render() - <Select> (react-select) レンダリング
    |
    └── SelectPreview (SelectPreview.js)
            +-- ListPreview - 複数選択のリスト表示
            └── WidgetPreviewContainer で値表示
```

### データフロー図

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

config.yml options  ──> convertToOption()       react-select options
                                                    |
                                            ユーザー選択
                                                    |
                                            handleChange()
                                                    |
                                                    v
                                            onChange(value | List)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `packages/decap-cms-widget-select/src/index.js` | ソース | ウィジェットエクスポート定義 |
| SelectControl.js | `packages/decap-cms-widget-select/src/SelectControl.js` | ソース | セレクトコントロール |
| SelectPreview.js | `packages/decap-cms-widget-select/src/SelectPreview.js` | ソース | プレビューコンポーネント |
| schema.js | `packages/decap-cms-widget-select/src/schema.js` | ソース | フィールドスキーマ定義 |
