# 画面設計書 12-ローディング画面

## 概要

本ドキュメントは、Decap CMS におけるローディング画面の画面設計書である。CMSアプリケーションの初期化時に設定ファイル（config.yml）を読み込んでいる間に表示されるローダー画面の仕様を定義する。

### 本画面の処理概要

ローディング画面は、Decap CMSの起動直後に設定ファイル（config.yml）をフェッチ・パース・バリデーション・適用する一連の初期化処理が完了するまでの間、ユーザーに待機状態を視覚的にフィードバックするための画面である。

**業務上の目的・背景**：CMSアプリケーションは起動時に設定ファイル（config.yml）をサーバーからフェッチし、内容のパース・バリデーション・デフォルト値の適用・ローカルバックエンドの検出等を行う必要がある。この処理にはネットワーク通信が伴うため時間がかかる可能性があり、その間ユーザーに処理中であることを明示するローディング画面が必要である。ローディング画面がなければ、ユーザーは空白画面を見ることになり、アプリケーションの動作状況が不明確になる。

**画面へのアクセス方法**：Decap CMSのURLにブラウザからアクセスした際、自動的に表示される。ユーザーが意図的にアクセスする画面ではなく、アプリケーション初期化プロセスの一部として自動的に描画される。configリデューサーのデフォルト状態（`isFetching: true`）により、初回レンダリング時に必ず表示される。

**主要な操作・処理内容**：
1. 回転するローダーアニメーション（CSSアニメーション）を画面中央に表示する
2. 「Loading configuration...」（英語）/「設定を読み込んでいます...」（日本語）のテキストをローダー下部に表示する
3. 設定ファイルの読み込みが完了するか、エラーが発生するまで表示を継続する

**画面遷移**：
- この画面に遷移してくる経路：ブラウザからCMSのURLにアクセスした際に最初に表示される
- この画面から遷移できる画面：
  - 設定読み込み成功 + 認証済み → コレクション一覧画面
  - 設定読み込み成功 + 未認証 → ログイン画面またはPKCE認証画面（バックエンド設定に依存）
  - 設定読み込みエラー → エラー画面（設定エラー）

**権限による表示制御**：権限やロールによる表示制御は存在しない。認証状態に関係なく、設定ファイル読み込み中は全てのユーザーに対して表示される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | アプリケーション初期化 | 主機能 | CMSアプリケーションの初期化中にLoaderコンポーネントを表示。config.isFetchingがtrueの間表示 |
| 2 | 設定管理 | 主機能 | 設定ファイル（config.yml）の読み込み処理中のローディング状態管理 |
| 70 | CMS UIローカライズ | 補助機能 | ローディングメッセージのローカライズ表示。`t('app.app.loadingConfig')`による多言語対応 |

## 画面種別

ローディング（状態表示）

## URL/ルーティング

- 特定のURLルートは割り当てられていない
- CMSの全URLにアクセスした際、`config.isFetching` が `true` の間に表示される
- configリデューサーのデフォルト状態（defaultState）が `{ isFetching: true }` であるため、初回レンダリング時に必ず表示される
- App.jsのrenderメソッド169-171行目の条件分岐で表示が制御される

## 入出力項目

本画面にはユーザー入力項目は存在しない。

| 項目名 | 入出力 | 型 | 説明 |
|--------|--------|------|------|
| config.isFetching | 入力（Redux state） | boolean | 設定ファイル読み込み中かどうかのフラグ。configリデューサーで管理。デフォルト値はtrue |
| t（翻訳関数） | 入力（props） | Function | react-polyglotの翻訳関数。HOC `translate()` により注入される |

## 表示項目

| 項目名 | 表示内容 | データソース | 備考 |
|--------|----------|-------------|------|
| ローダーアニメーション | 回転する円形インジケーター | CSSアニメーション（`@emotion/react` keyframes） | 0.6秒で1回転、linear、無限ループ。activeプロパティがtrueの場合に表示。疑似要素before（灰色の静的円）とafter（青色の回転円）で構成 |
| ローディングテキスト | 「Loading configuration...」/「設定を読み込んでいます...」 | ロケールファイル `t('app.app.loadingConfig')` | LoaderTextスタイルドコンポーネント（色: #767676、上マージン: 55px）内に描画 |

## イベント仕様

### 1-設定ファイル読み込み開始

- **トリガー**: アプリケーション初期化時にAppコンポーネントがマウントされ、`loadConfig` アクションがディスパッチされる
- **処理内容**:
  1. `configLoading()` アクション（`CONFIG_REQUEST`）がディスパッチされ、configリデューサーの`isFetching`が`true`に設定される（reducers/config.ts 15-17行目）
  2. App.jsのrenderメソッド169-170行目の条件分岐 `if (config.isFetching)` が`true`となり、Loaderコンポーネントがレンダリングされる
  3. `<Loader active>{t('app.app.loadingConfig')}</Loader>` によりアクティブ状態のローダーが表示される

### 2-設定ファイル読み込み完了（成功）

- **トリガー**: 設定ファイルのフェッチ・パース・バリデーション・デフォルト値適用が全て成功
- **処理内容**:
  1. `configLoaded(config)` アクション（`CONFIG_SUCCESS`）がディスパッチされる（actions/config.ts 548行目）
  2. configリデューサーで`isFetching`が`false`に設定される（reducers/config.ts 19-23行目）
  3. App.jsのrenderメソッドで`config.isFetching`が`false`になるため、Loaderが非表示になる
  4. ユーザーの認証状態に応じて次の画面（認証画面またはコレクション一覧画面）に遷移する

### 3-設定ファイル読み込み失敗

- **トリガー**: 設定ファイルのフェッチ・パース・バリデーションのいずれかでエラーが発生
- **処理内容**:
  1. `configFailed(err)` アクション（`CONFIG_FAILURE`）がディスパッチされる（actions/config.ts 554行目）
  2. configリデューサーで`isFetching`が`false`に、`error`にエラーメッセージ文字列が設定される（reducers/config.ts 24-26行目）
  3. App.jsのrenderメソッドで`config.error`が真値になるため、165-167行目の`configError(config)`メソッドが呼ばれてエラー画面（設定エラー）が表示される

## データベース更新仕様

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

本画面にはデータベースへの操作は存在しない。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| なし | - | - | 本画面は状態表示のみで、データベース操作を行わない |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容（英語） | メッセージ内容（日本語） | 表示条件 |
|-------------|------|----------------------|----------------------|---------|
| app.app.loadingConfig | 情報 | Loading configuration... | 設定を読み込んでいます... | config.isFetchingがtrueの間表示 |

## 例外処理

- **設定ファイル読み込み失敗**: `config.yml` のフェッチに失敗した場合（ネットワークエラー、404等）、`CONFIG_FAILURE` アクションがディスパッチされ、ローディング画面からエラー画面（設定エラー）に切り替わる
- **設定ファイルのバリデーションエラー**: `validateConfig` で設定内容が不正と判定された場合、同様に `CONFIG_FAILURE` に遷移する
- **configがnullの場合**: App.jsの161-163行目で `config === null` の場合は何も表示しない（`return null`）。これはconfigリデューサーの初期化前の状態に対するガード
- **手動設定のみ（load_config_file=false）の場合**: actions/config.ts 534行目で`load_config_file === false`の場合はYAMLフェッチをスキップし空オブジェクトを使用するため、フェッチエラーは発生しない

## 備考

- Loaderコンポーネントは `decap-cms-ui-default` パッケージで提供される共通UIコンポーネントである
- Loaderは `@emotion/styled` と `@emotion/react` を使用してスタイリングされている
- ローダーアニメーションはCSS keyframes（`rotate(0deg)` → `rotate(360deg)`、0.6秒、linear、infinite）で実装されている（Loader.js 33-41行目、155-156行目）
- Loaderコンポーネントは `active` プロパティで表示/非表示を制御する（`display: block` / `display: none`、Loader.js 125行目）
- Loaderの位置は `position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%)` により画面中央に配置される（Loader.js 126-132行目）
- z-indexは `zIndex.zIndex1000`（= 1000）が適用されており、他の要素の上に表示される（Loader.js 131行目、styles.js 428行目）
- Loaderコンポーネントには複数のchildren（配列）を渡すことで、5秒ごとにメッセージを切り替えるローテーション機能がある（Loader.js 84-88行目）が、本画面では単一の文字列のみが渡されるためローテーションは行われない
- configリデューサーのdefaultStateは `{ isFetching: true }` であるため、アプリケーションの初回マウント時には必ずローディング画面が表示される（reducers/config.ts 9-11行目）
- ローダーの回転円は疑似要素`::before`（灰色の静的な円、border-color: rgba(0, 0, 0, 0.1)）と`::after`（青色の回転する円、border-color: colors.active transparent transparent）で構成される

---

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

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

### 推奨読解順序

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

ローディング画面の表示制御に関わるRedux stateの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | config.ts（リデューサー） | `packages/decap-cms-core/src/reducers/config.ts` | 9-11行目: defaultStateの`isFetching: true`を確認。13-28行目: CONFIG_REQUEST/SUCCESS/FAILUREに対するstate遷移を理解 |
| 1-2 | config.ts（アクション） | `packages/decap-cms-core/src/actions/config.ts` | 32-34行目: アクションタイプ定数（CONFIG_REQUEST/SUCCESS/FAILURE）の定義。423-442行目: `configLoading`/`configLoaded`/`configFailed` アクションクリエイター |
| 1-3 | index.js（英語ロケール） | `packages/decap-cms-locales/src/en/index.js` | 29行目: `loadingConfig: 'Loading configuration...'` の定義を確認 |
| 1-4 | index.js（日本語ロケール） | `packages/decap-cms-locales/src/ja/index.js` | 29行目: `loadingConfig: '設定を読み込んでいます...'` の定義を確認 |

**読解のコツ**: Decap CMSではReduxを使用した状態管理を行っている。configリデューサーの`isFetching`プロパティがローディング画面の表示トリガーとなる。defaultStateが`{ isFetching: true }`であることが重要なポイントで、これにより初回レンダリング時に必ずローディング画面が表示される仕組みになっている。リデューサーは`immer`の`produce`を使用してイミュータブルな状態更新を実現している。

#### Step 2: エントリーポイントを理解する

ローディング画面の表示判定はApp.jsのrenderメソッド内で行われる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | App.js | `packages/decap-cms-core/src/components/App/App.js` | 169-171行目: `config.isFetching`チェックとLoaderレンダリング。261-278行目: `mapStateToProps`でRedux stateからconfig情報を取得 |

**主要処理フロー**:
1. **147行目（renderメソッド開始）**: props（user, config, collections等）をデストラクチャリング
2. **161-163行目**: `config === null` の場合は`null`を返す（初期化前のガード）
3. **165-167行目**: `config.error` が存在する場合はエラー画面を表示
4. **169-171行目**: `config.isFetching` が`true`の場合、`<Loader active>{t('app.app.loadingConfig')}</Loader>` を返す -- ローディング画面の描画箇所
5. **173-175行目**: `user == null` の場合は認証画面を表示
6. **177行目以降**: 認証済みの場合はメイン画面（ルーティング）を描画

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Loader.js | `packages/decap-cms-ui-default/src/Loader.js` | コンポーネント全体（163行）を確認。StyledLoaderがデフォルトエクスポートされ、位置・サイズ・アニメーションのスタイルが定義されている |

**主要処理フロー**:
- **9-30行目**: CSSスタイル定義（disabled/active/enter/enterActive/exit/exitActive）- CSSTransition用
- **32-42行目**: keyframesアニメーション定義（`rotate(0deg)` → `rotate(360deg)`）
- **44-51行目**: LoaderTextスタイルドコンポーネント（テキスト表示領域、色: #767676、上マージン: 55px）
- **53-57行目**: LoaderItemスタイルドコンポーネント（配列children時の個別アイテム表示用）
- **59-122行目**: Loaderクラスコンポーネント
  - **65-67行目**: state初期値（currentItem: 0）
  - **80-89行目（setAnimation）**: 配列childrenの場合に5秒間隔のローテーションインターバルを設定
  - **91-116行目（renderChild）**: childrenの型に応じた表示制御。文字列の場合はLoaderText内にそのまま表示（96-97行目）。配列の場合は5秒ごとのローテーション表示（98-114行目）
  - **118-121行目（render）**: classNameを適用してrenderChildの結果を描画
- **124-160行目**: StyledLoader（デフォルトエクスポート）
  - **125行目**: `display: ${props => (props.active ? 'block' : 'none')}` でactive制御
  - **126-132行目**: 画面中央配置（`position: absolute; top: 50%; left: 50%; transform: translateX(-50%) translateY(-50%)`）
  - **134-146行目**: before疑似要素（静的な灰色円）とafter疑似要素（回転する青色円）
  - **155行目**: `animation: ${animations.loader} 0.6s linear`
  - **156行目**: `animation-iteration-count: infinite`
  - **157行目**: `border-color: ${colors.active} transparent transparent` で上部のみ着色

#### Step 4: 設定ファイル読み込み処理の流れを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | config.ts（アクション） | `packages/decap-cms-core/src/actions/config.ts` | 523-558行目: `loadConfig`関数。configLoading → getConfigYaml → validateConfig → handleLocalBackend → normalizeConfig → applyDefaults → configLoaded の一連のフローを理解 |

**主要処理フロー**:
- **523行目**: `loadConfig(manualConfig, onLoad)` 関数定義
- **524-526行目**: `window.CMS_CONFIG`が存在する場合は即座にconfigLoadedを返す
- **528行目**: `dispatch(configLoading())` でCONFIG_REQUESTをディスパッチ（isFetching = true）
- **531行目**: `getConfigUrl()` で設定ファイルのURLを取得（デフォルト: 'config.yml'）
- **533-536行目**: `getConfigYaml()` で設定ファイルをフェッチ・パース
- **539行目**: `deepmerge` で手動設定とマージ
- **541行目**: `validateConfig` でバリデーション
- **543行目**: `handleLocalBackend` でローカルバックエンド検出
- **544行目**: `normalizeConfig` で設定正規化
- **546行目**: `applyDefaults` でデフォルト値適用
- **548行目**: `dispatch(configLoaded(config))` でCONFIG_SUCCESSをディスパッチ（isFetching = false）
- **553-554行目**: エラー時 `dispatch(configFailed(err))` でCONFIG_FAILUREをディスパッチ（isFetching = false, error設定）

#### Step 5: グローバルUI状態を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | globalUI.ts | `packages/decap-cms-core/src/reducers/globalUI.ts` | 全体: REQUEST/SUCCESS/FAILUREアクションに連動するグローバルなisFetchingフラグの管理を確認。App.jsのisFetchingプロップはこちらのリデューサーから取得される |

**主要処理フロー**:
- **12-17行目**: LOADING_IGNORE_LIST定義。DEPLOY_PREVIEW/STATUSアクションはisFetching制御から除外
- **31-43行目**: globalUIリデューサー。アクションタイプ名に'REQUEST'が含まれればisFetching=true、'SUCCESS'/'FAILURE'が含まれればisFetching=false
- App.jsのmapStateToProps（264行目）で`globalUI.isFetching`を取得し、TopBarProgressの表示制御に使用（197行目）。config.isFetchingとは異なるフラグであることに注意

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

```
App.js (render) [147行目]
    |
    +-- config === null? --> return null [161行目]
    |
    +-- config.error? --> configError() --> エラー画面（設定エラー） [165行目]
    |
    +-- config.isFetching? --> <Loader active>  <-- 本画面 [169行目]
    |       |
    |       +-- StyledLoader (decap-cms-ui-default/Loader.js 124行目)
    |       |       |
    |       |       +-- Loader (クラスコンポーネント) [59行目]
    |       |               |
    |       |               +-- renderChild() [91行目]
    |       |                       |
    |       |                       +-- typeof children == 'string' [96行目]
    |       |                               |
    |       |                               +-- LoaderText [44行目]
    |       |                                       |
    |       |                                       +-- t('app.app.loadingConfig')
    |       |                                               |
    |       |                                               +-- react-polyglot --> ロケールファイル参照
    |       |
    |       +-- CSSアニメーション（keyframes: rotate 0.6s linear infinite）[33-41行目, 155-156行目]
    |       +-- 疑似要素 ::before（静的灰色円）[149-151行目]
    |       +-- 疑似要素 ::after（回転青色円）[154-158行目]
    |
    +-- user == null? --> authenticating() --> ログイン画面/PKCE認証画面 [173行目]
    |
    +-- (認証済み) --> Header + Switch (ルーティング) [180行目以降]

--- バックグラウンド処理 ---
loadConfig() (actions/config.ts 523行目)
    |
    +-- configLoading() --> CONFIG_REQUEST --> config.isFetching = true [528行目]
    |
    +-- getConfigYaml() --> config.ymlフェッチ [533-536行目]
    |       |
    |       +-- fetch(configUrl) --> レスポンス取得 [404行目]
    |       +-- parseConfig() --> YAMLパース（yamlライブラリ使用）[420行目]
    |
    +-- deepmerge() --> 手動設定とマージ [539行目]
    |
    +-- validateConfig() --> 設定バリデーション [541行目]
    |
    +-- handleLocalBackend() --> ローカルバックエンド検出 [543行目]
    |
    +-- normalizeConfig() --> 設定正規化 [544行目]
    |
    +-- applyDefaults() --> デフォルト値適用 [546行目]
    |
    +-- configLoaded(config) --> CONFIG_SUCCESS --> config.isFetching = false [548行目]
    |
    +-- (エラー時) configFailed(err) --> CONFIG_FAILURE --> config.isFetching = false, error設定 [554行目]
```

### データフロー図

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

アプリケーション起動  ───> loadConfig()                 ───> CONFIG_REQUEST
                          (actions/config.ts 523行目)         ディスパッチ
                                |
                                v
                          configリデューサー              ───> state.config.isFetching = true
                          (reducers/config.ts 15-16行目)
                                |
                                v
                          App.js render()                ───> Loaderコンポーネント
                          (169行目: config.isFetching)         レンダリング
                                |
                                v
ロケール設定         ───> t('app.app.loadingConfig')     ───> "Loading configuration..."
(config.locale)           (react-polyglot)                    テキスト表示
                                |
                                v
config.yml           ───> フェッチ・パース・バリデーション ───> CONFIG_SUCCESS
(サーバー上)               (actions/config.ts 530-548行目)     ディスパッチ
                                |
                                v
                          configリデューサー              ───> state.config.isFetching = false
                          (reducers/config.ts 19-22行目)
                                |
                                v
                          App.js render()                ───> ローディング画面非表示
                          (169行目: 条件不成立)                -> 次の画面へ遷移

--- エラー時 ---
config.yml           ───> フェッチ/パース/バリデーション  ───> CONFIG_FAILURE
(読み込み失敗)              エラー発生                         ディスパッチ
                                |
                                v
                          configリデューサー              ───> state.config.isFetching = false
                          (reducers/config.ts 25-26行目)       state.config.error = エラー文字列
                                |
                                v
                          App.js render()                ───> エラー画面表示
                          (165行目: config.error)              configError()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| App.js | `packages/decap-cms-core/src/components/App/App.js` | ソース | ローディング画面の表示判定。169-171行目でconfig.isFetching時にLoaderをレンダリング |
| Loader.js | `packages/decap-cms-ui-default/src/Loader.js` | ソース | ローダーUIコンポーネント（全163行）。回転アニメーションとテキスト表示を提供 |
| config.ts（リデューサー） | `packages/decap-cms-core/src/reducers/config.ts` | ソース | configのRedux state管理。isFetchingフラグのdefaultState（true）と状態遷移を定義 |
| config.ts（アクション） | `packages/decap-cms-core/src/actions/config.ts` | ソース | 設定ファイル読み込みアクション。loadConfig/configLoading/configLoaded/configFailedの定義 |
| globalUI.ts | `packages/decap-cms-core/src/reducers/globalUI.ts` | ソース | グローバルUI状態のリデューサー。REQUEST/SUCCESS/FAILUREアクションに連動するisFetching管理 |
| styles.js | `packages/decap-cms-ui-default/src/styles.js` | ソース | UI共通スタイル定数。colors（67行目: active）、zIndex（428行目: zIndex1000）等のLoaderで使用されるスタイル値を定義 |
| index.js（英語） | `packages/decap-cms-locales/src/en/index.js` | ロケール | 英語ロケールファイル。29行目: `loadingConfig: 'Loading configuration...'` |
| index.js（日本語） | `packages/decap-cms-locales/src/ja/index.js` | ロケール | 日本語ロケールファイル。29行目: `loadingConfig: '設定を読み込んでいます...'` |
| history.ts | `packages/decap-cms-core/src/routing/history.ts` | ソース | HashHistoryの生成。ルーティング基盤 |
