# 機能設計書 52-文字列ウィジェット

## 概要

本ドキュメントは、Decap CMSにおける文字列ウィジェット（String Widget）の設計を記述する。単行のプレーンテキスト入力フィールドを提供するウィジェットである。

### 本機能の処理概要

**業務上の目的・背景**：CMSでコンテンツを編集する際に、タイトル・著者名・URLなど単行のテキスト入力が必要なフィールドに対して、シンプルなテキスト入力UIを提供する。Decap CMSのウィジェットシステムにおいて最も基本的な入力ウィジェットの一つであり、config.ymlで`widget: string`と指定することで利用される。

**機能の利用シーン**：エントリ編集画面・エントリ新規作成画面において、`widget: string`が指定されたフィールドに対して`<input type="text">`要素をレンダリングする。ネストされたウィジェット（Markdownウィジェット内の画像altテキスト等）としても利用される。

**主要な処理内容**：
1. テキスト入力フィールド（`<input type="text">`）のレンダリング
2. 入力値の変更時にonChangeコールバックで親コンポーネントに値を通知
3. カーソル位置の維持（ネストされたウィジェットでのカーソルジャンプ防止）
4. プレビューコンポーネントでの値表示

**関連システム・外部連携**：なし（自己完結型ウィジェット）

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

## 関連画面

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

## 機能種別

UI入力コンポーネント

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| onChange | function | Yes | 値変更時のコールバック関数 | - |
| forID | string | No | input要素のid属性 | - |
| value | node | No | 現在の入力値（デフォルト: ''） | - |
| classNameWrapper | string | Yes | ラッパーのCSSクラス名 | - |
| setActiveStyle | function | Yes | フォーカス時のスタイル設定関数 | - |
| setInactiveStyle | function | Yes | ブラー時のスタイル設定関数 | - |

### 入力データソース

エディタのコントロールペインから提供されるprops。Reduxストアのエントリドラフトから値が供給される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| value | string | ユーザーが入力したテキスト値 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. コンポーネントマウント
   └─ PropTypesの手動バリデーション実行（React 19対応）
2. ユーザーがテキスト入力
   └─ handleChangeメソッドが呼ばれる
3. カーソル位置を_selに保存
   └─ e.target.selectionStartを記録
4. onChangeコールバックで値を親に通知
   └─ onChange(e.target.value)
5. componentDidUpdateでカーソル位置を復元
   └─ _el.setSelectionRange(_sel, _sel)
```

### フローチャート

```mermaid
flowchart TD
    A[input要素レンダリング] --> B[ユーザー入力]
    B --> C[handleChange呼び出し]
    C --> D[カーソル位置を_selに保存]
    D --> E[onChange で値を通知]
    E --> F[React再レンダリング]
    F --> G[componentDidUpdate]
    G --> H{カーソル位置ずれ?}
    H -->|Yes| I[setSelectionRangeで復元]
    H -->|No| J[処理完了]
    I --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-52-01 | デフォルト値 | valueが未指定の場合は空文字列をデフォルト値とする | value prop未指定時 |
| BR-52-02 | カーソル位置維持 | ネストされた入力フィールドでカーソルが末尾にジャンプしないよう、selectionStartを保持・復元する | 常時 |
| BR-52-03 | null安全 | value || '' でnull/undefinedを空文字に変換してレンダリング | 常時 |

### 計算ロジック

特になし。

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

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

該当なし。本ウィジェットはUIコンポーネントであり、データベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | PropType警告 | 必須propsが未提供の場合 | React 19対応のためcomponentDidMountで手動バリデーション |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

特になし。componentDidUpdateでカーソル位置を毎回チェックするが、DOMアクセスは最小限に抑えられている。

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

HTMLのinput type="text"要素を使用しているため、ブラウザのデフォルトのXSS対策が適用される。

## 備考

- GitHub Issue #4539, #3578 に記載されたカーソルジャンプバグへの対策が実装されている
- Reactクラスコンポーネントとして実装されている

---

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

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

### 推奨読解順序

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

ウィジェットのエクスポート構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `packages/decap-cms-widget-string/src/index.js` | Widget関数がname, controlComponent, previewComponentを返す構造（4-11行目） |

**読解のコツ**: Decap CMSのウィジェットは必ず`Widget`ファクトリ関数、`controlComponent`（入力用）、`previewComponent`（プレビュー用）の3つをエクスポートする。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | StringControl.js | `packages/decap-cms-widget-string/src/StringControl.js` | Reactクラスコンポーネントとしての実装全体（4-66行目） |

**主要処理フロー**:
1. **6-12行目**: PropTypes定義（onChange, forID, value, classNameWrapper, setActiveStyle, setInactiveStyle）
2. **14-16行目**: デフォルトprops（value: ''）
3. **19行目**: _sel = 0（カーソル位置保持用変数）
4. **22行目**: _el = null（input要素の参照保持）
5. **24-27行目**: componentDidMount - React 19対応のPropTypes手動チェック
6. **37-41行目**: componentDidUpdate - カーソル位置復元ロジック
7. **43-46行目**: handleChange - selectionStartを保存してからonChangeを呼び出し
8. **48-65行目**: render - input type="text"要素のレンダリング

#### Step 3: プレビューコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | StringPreview.js | `packages/decap-cms-widget-string/src/StringPreview.js` | WidgetPreviewContainerで値を表示するシンプルな関数コンポーネント（5-7行目） |

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

```
DecapCmsWidgetString (index.js)
    |
    +-- Widget()
    |       └── { name: 'string', controlComponent, previewComponent }
    |
    +-- StringControl (StringControl.js)
    |       +-- componentDidMount() - PropTypes手動検証
    |       +-- componentDidUpdate() - カーソル位置復元
    |       +-- handleChange() - 値変更ハンドラ
    |       └── render() - <input type="text"> レンダリング
    |
    └── StringPreview (StringPreview.js)
            └── WidgetPreviewContainer で値表示
```

### データフロー図

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

ユーザーのキー入力  ──> handleChange()          onChange(value)
                        |                        |
                        v                        v
                   selectionStart保存      Reduxドラフト更新
                        |
                        v
                   componentDidUpdate
                        |
                        v
                   カーソル位置復元
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `packages/decap-cms-widget-string/src/index.js` | ソース | ウィジェットエクスポート定義 |
| StringControl.js | `packages/decap-cms-widget-string/src/StringControl.js` | ソース | 入力コントロールコンポーネント |
| StringPreview.js | `packages/decap-cms-widget-string/src/StringPreview.js` | ソース | プレビューコンポーネント |
