kenju's blog

About Programming, Mathematics, Security and Blockchain

Draft.js における keydown イベントの処理の流れの詳説

tl;dr

  • エディターのキー入力に対するテストを書く中で、Draft.js の内部実装における KeyboardEvent -> keyDown の処理の流れを理解する必要があったので調べた
  • keyDown イベントは src/handlers/edit/editOnKeyDown.js で処理されている
  • onKeyDowndiv > div.editorContainer > div.public/DraftEditor/content にアタッチされている

詳説

Draft.js にはEditor上で処理する内容に応じて、5つのモード が存在する

  1. edit
  2. composite
  3. drag
  4. cut
  5. render

このうち、edit, composite, drag で提供されるロジックは、以下の handlers/ 配下のディレクトリに対応している:

src/
-- component/
    -- base/
        -- DraftEditor.react.js: ベースとなるコンポーネント
    -- handlers/
        -- composition/
            -- DraftEditorCompositionHandler.js
        -- drag/
            -- DraftEditorDragHandler.js
        -- edit/
            -- DraftEditorEditHandler.js

それぞれのアクションハンドラーは、対応するイベントとそれが発生した時の処理内容を定義している。例えば、DraftEditorEditHandler.js は以下のようなファイルとなっている

// DraftEditorEditHandler.js
// 同じディレクトリに配置される "editOn***.js" というファイルへのファサードとして機能している
const DraftEditorEditHandler = {
  onBeforeInput,
  onBlur,
  onCompositionStart,
  onCopy,
  onCut,
  onDragOver,
  onDragStart,
  onFocus,
  onInput,
  onKeyDown,
  onPaste,
  onSelect,
};

そのうち、onKeyDown イベントは handlers/edit/editOnKeyDown.js にて実装されている。基本的には

  1. Draft.js がデフォルトで提供しているKeyが入力されたかどうかを判定
  2. ユーザーが ‘keyBindingFn’ で定義した独自のKey入力ロジックかどうかを判定
  3. editor.props.handleKeyCommand() にコマンドを渡して独自のKey入力ロジックを呼び出す
  4. editor.update() でEditorの内容を更新

ということをやっている。

function editOnKeyDown(editor: DraftEditor, e: SyntheticKeyboardEvent): void {
  // 1. Draft.js がデフォルトで提供しているKeyが入力されたかどうかを判定
  switch (keyCode) {
    case Keys.RETURN:
      e.preventDefault();
      editor.props.handleReturn(e, editorState); // このように 'editor.props.handle***()' という形式でハンドラーが実装され、EditorのProps経由で呼び出す
    ...
  }

  // 2. ユーザーが 'keyBindingFn' で定義した独自のKey入力ロジックかどうかを判定
  var command = editor.props.keyBindingFn(e);

  // 3. editor.props.handleKeyCommand() にコマンドを渡して独自のKey入力ロジックを呼び出す
  if (
    editor.props.handleKeyCommand &&
    isEventHandled(editor.props.handleKeyCommand(command, editorState))
  ) {
    return;
  }

  // 4. editor.update() でEditorの内容を更新
  var newState = onKeyCommand(command, editorState);
  if (newState !== editorState) {
    editor.update(newState);
  }
}

Draft.js で独自のキーボードショートカット処理を実装する場合、基本的には #Key Bindings の公式チュートリアルに沿って実装する。このチュートリアル内部で、たった今紹介した keyBindingFn, handleKeyCommand に触れられている。

最後に、この keyDown イベントはどのDOM Element にアタッチされているかだが、

// facebook/draft-js/blob/master/src/component/base/DraftEditor.react.js
class DraftEditor extends React.Component {
  ...
  render(): React.Element<any> {
    ...
    return (
      <div className={rootClass}>
        {this._renderPlaceholder()}
        <div
          className={cx('DraftEditor/editorContainer')}
          ref="editorContainer">
          <div
            ...
            className={cx({
              'notranslate': !readOnly,
              'public/DraftEditor/content': true,
            })}
            onCopy={this._onCopy}
            onCut={this._onCut}
            onDragEnd={this._onDragEnd}
            ...
            onKeyDown={this._onKeyDown}

とあるように、div > div.editorContainer > div.public/DraftEditor/content にアタッチされている。