Draft.jsのデータ構造

[Draft.js](https://github.com/facebook/draft-js)がリッチテキストを表現するために、どのようにメタデータを保持しているか実例を踏まえて解説します。(0.10時点）

Draft.jsを単純に使う分にはあまり意識する必要はありませんが、拡張して独自のstyleを表現したりするにはこの辺りを理解しておく必要があります。

# データ構造
メタデータはconvertToRaw()というメソッドを使うことで取得することができます。

blocksというBlockのArrayと
entityMapというEntityのMapからできています。

```javascript
    export type RawDraftContentState = {
      blocks: Array<RawDraftContentBlock>;
      entityMap: { [key: string]: RawDraftEntity };
    };
```
entityMapのkeyはEntityのidentifierとなります。


## Block

Blockはコンテンツの一塊で、これらが集まったものが文章になるといったイメージです。HTMLでいうとパラグラフに近いです。

Blockは以下のような要素で構成されています。

```javascript
    export type RawDraftContentBlock = {
      key: ?string;
      type: DraftBlockType;
      text: string;
      depth: ?number;
      inlineStyleRanges: ?Array<InlineStyleRange>;
      entityRanges: ?Array<EntityRange>;
      data?: Object;
    };
```

- key
  - blockのidentifier
- type
  - Blockの種類
    - デフォルトで用意されているtype
      - unstyled
      - paragraph
      - header-one, header-two, header-three, header-four, header-five, header-six
      - unordered-list-item
      - ordered-list-item
      - blockquote
      - code-block
      - atomic
    - 独自のタイプを加えることも可能
- text
  - Block内に記述されたテキスト
- depth
  - unordered-list-itemやordred-list-itemでインデントしたときに使われる深さ
- inlineStyleRanges
  - boldなどといったinline styleをテキストのどこに反映するかといった情報を持ったArray
    - InlineStyleRange
      - InlineStyleRangeはstyleとoffsetとlengthを持ち、これらの情報からどの位置にどのstyleを反映するかわかります
- entityRanges
  - Entityをテキストのどこに反映するかといった情報を持ったArray
    - EntityRange
      - EntityRangeはkeyとoffsetとlengthを持ち、これらの情報からどの位置にどのEntityを反映するかわかります。keyはEntityのidentifierにあたります。
- data
  - blockにメタデータを持たせたいときに使います。


## Entity

Entityはテキストを装飾するのにmeta dataを必要とする場合に用います。(inline styleのようにmeta dataが必要ない、つまりtypeだけわかればよい場合はinline styleを用います）

Entityは以下のような要素で構成されています。

```javascript
    export type RawDraftEntity = {
      type: DraftEntityType;
      mutability: DraftEntityMutability;
      data: ?{ [key: string]: any };
    };
```

- type
  - Entityの種類
- mutability
  - Entityを適用するテキストが変更可能かどうかを示します
    - MUTABLE
    - IMMUTABLE
    - SEGMENTED
- data
  - Entityを適用するのに必要なデータをMapで保持します。
    - 例えばLINKというtypeの場合だとLINKにはurlが必要なので
      - `{url:` `"``http://google.com``"``}`といったデータにします。


# 実例
## Block全体のスタイルを変える場合

引用やコードブロックなどのようにBlock全体のスタイルを変更する場合のデータ構造の使われ方です。

「引用です」という文字列でできたBlockを引用にしたときのデータ構造はこのようになります。

```json
    {
        "entityMap": {},
        "blocks": [
            {
                "key": "1r71t",
                "text": "引用です",
                "type": "blockquote",
                "depth": 0,
                "inlineStyleRanges": [],
                "entityRanges": [],
                "data": {}
            }
        ]
    }
```

blockは「引用です」というtextを持ち、typeにblockquoteeを適用するというふうに設定されています。


## Blockの中の文字列の一部のスタイルを変える場合

太字や斜体などBlockの中の文字列の一部のスタイルを変える場合のデータ構造の使われ方です。

「これが太字です」という文字列の中の太字という文字に太字を設定したときのデータ構造はこのようになります。

```javascript
    {
        "entityMap": {},
        "blocks": [
            {
                "key": "4atgo",
                "text": "これが太字です",
                "type": "unstyled",
                "depth": 0,
                "inlineStyleRanges": [
                    {
                        "offset": 3,
                        "length": 2,
                        "style": "BOLD"
                    }
                ],
                "entityRanges": [],
                "data": {}
            }
        ]
    }
```

blockは「これが太字です」というtextを持ち、inlineStyleRangeで3文字目から2文字の範囲の文字にBOLDのstyleを適用するというふうに設定されています。

また、LINKの場合はstyleだけでなくurlといった別のメタデータも必要となるのでinline styleでは対応できません。

## Blockの中の文字列の一部に特殊な装飾を加える場合

LINKなどBlockの中の文字列の一部のに特殊な装飾を加える場合のデータ構造の使われ方です。

「これがLINKです」という文字列の中のLINKという文字にlinkを設定したときのデータ構造はこのようになります。

```javascript
    {
        "entityMap": {
            "0": {
                "type": "LINK",
                "mutability": "IMMUTABLE",
                "data": {
                    "url": "https://google.com"
                }
            }
        },
        "blocks": [
            {
                "key": "4atgo",
                "text": "これがLINKです",
                "type": "unstyled",
                "depth": 0,
                "inlineStyleRanges": [],
                "entityRanges": [
                    {
                        "offset": 3,
                        "length": 4,
                        "key": 0
                    }
                ],
                "data": {}
            }
        ]
    }
```

0というEntityにtypeがLINK、dataにlink先のurlが設定されています。

blockは「これがLINKです」というtextを持ち、enittyRangeで3文字目から4文字の範囲の文字をEntityの0を適用するというふうに設定されています。


## Blockでテキスト以外にも情報を使って表現したい場合

文章の中に画像を入れたい場合に、Blockを画像にする場合にBlockに独自のtypeを追加してdataを用いるという方法があります。

```javascript
    {
        "entityMap": {},
        "blocks": [
            {
                "key": "7va9s",
                "text": "",
                "type": "IMAGE",
                "depth": 0,
                "inlineStyleRanges": [],
                "entityRanges": [],
                "data": {
                    "src": "http://example.com/images/example1.jpg"
                }
            },
        ]
    }
```

typeにIMAGEという値を設定し、dataに画像ファイルのパスを設定しています。

これらのtypeやdataには任意の値を設定することができるので、どのようなBlockを表現したいかの用途によって決めるとよいです。これらの値を元にBlockの表示を行うCustom Blockを実装します。Custom Blockの実装方法は別の記事で説明したいと思います。