LoginSignup
23

More than 5 years have passed since last update.

draft-jsでシンタックスハイライターを作る

Last updated at Posted at 2016-07-09

tl;dr

strategy で 正規表現でヒットさせて、ヒットした部分を component で置き換える。正規表現と色の組み合わせは一般化できるので、そこまでやってみた。

最小コード

@mizchi みたいにユーザー名に色を付ける

decorators/username-decorator.js
import {CompositeDecorator} from "draft-js";
const USERNAME_REGEX = /\@[\w]+/g;

export default new CompositeDecorator([
  {
    strategy(contentBlock, callback) {
      const text = contentBlock.getText();
      let matchArr, start;
      while ((matchArr = USERNAME_REGEX.exec(text)) !== null) {
        start = matchArr.index;
        callback(start, start + matchArr[0].length);
      }
    },
    component(props) {
      return <span {...props} style={styles.handle}>{props.children}</span>;
    },
  },
]);

const styles = {
  handle: {
    color: "rgba(98, 177, 254, 1.0)",
    direction: "ltr",
    unicodeBidi: "bidi-override",
  }
};

使う

components/my-editor.js
import {Component} from "react";
import {Editor, EditorState} from "draft-js";
import usernameDecorator from "../decorators/tweet-decorator";

export default class MyEditor extends Component {
  constructor() {
    super();
    this.state = {
      editorState: EditorState.createEmpty(usernameDecorator),
    };
  }

  onChange(editorState) {
    this.setState({editorState});
  }

  render() {
    return (
      <Editor
        editorState={this.state.editorState}
        onChange={this.onChange.bind(this)}
        ref="editor"
      />
    );
  }
}

動きました

一般化する

任意の正規表現でヒットしたものをハイライトするのを一般化するとこうなる。

import {CompositeDecorator} from "draft-js";

const HIGHLIGHT_RULES = [
  {
    regex: /\@[\w]+/g,
    color: "rgb(98, 177, 254)"
  },
  {
    regex: /\#[\w]+/g,
    color: "rgb(95, 184, 138)"
  }
];

export default new CompositeDecorator(HIGHLIGHT_RULES.map(rule => (
  {
    strategy(contentBlock, callback) {
      const text = contentBlock.getText();
      let matchArr, start;
      while ((matchArr = rule.regex.exec(text)) !== null) {
        start = matchArr.index;
        callback(start, start + matchArr[0].length);
      }
    },
    component(props) {
      return <span {...props} style={{
        color: rule.color,
        direction: "ltr",
        unicodeBidi: "bidi-override",
      }}>{props.children}</span>;
    },
  }
)));

まだ使い込んでないんで色々と引数を足したくなるだろうが、そのうちライブラリに切り出す。

特定の構文下でルールを変える、といった拡張が考えられる。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23