wysiwygエディタではなく、マークダウンエディタをReactでつくってみました。
ソースコードの大部分はReact入門を参考にさせていただきました。
雑なgifサンプルはこちら(:3」∠)
markedとhighlight.jsをbowerでインストール
bower install marked
bower install highlightjs
それぞれご自分の環境にインストールしてパスの設定までしておいてください。
bower install highlightではなく、highlightjsです。
両者は別物ようで、私はこれを間違えていたせいて小一時間ハマりました・・・・(泣)
htmlはこんな感じで↓
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="path/to/monokai.css" rel="stylesheet">
<link href="path/to/style.css" rel="stylesheet">
</head>
<body>
<div class="markdown-component">
<h1>React Markdown Editor</h1>
<div id="content"></div>
</div><!-- .component -->
<!-- scripts -->
<script src="path/to/react.js"></script>
<script src="path/to/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="path/to/marked.min.js"></script>
<script src="path/to/highlight.pack.min.js"></script>
<script type="text/babel" src="path/to/markdown.js"></script>
</body>
</html>
シンタックスハイライトに使用するカラーテーマはmonokaiが好きなのでmonokaiのスタイルシートを設定しました。
babelについては今回CDNを使用していますが、bowerでインストールしてもOKです。
さて、Reactコンポーネントを作っていきますが、冒頭でも述べたように、大部分はReact入門を参考にしているので、こちらを一読しておくとよろしいかと思います。
参考ソースにhighlight.jsの設定コードだけ追加した感じです。(全然仕事していないww)
markdown.js
var App = React.createClass({
getInitialState: function() {
return {
markdown: ""
};
},
updateMarkdown: function(markdown) {
this.setState({
markdown: markdown
});
},
render: function() {
return (
<div>
<TextInput onChange = {this.updateMarkdown}/>
<Markdown markdown = {this.state.markdown}/>
</div>
);
}
});
var TextInput = React.createClass({
propTypes: {
onChange: React.PropTypes.func.isRequired
},
_onChange: function(e) {
this.props.onChange(e.target.value);
},
render: function() {
return (
<textarea onChange = {this._onChange}></textarea>
);
}
});
var Markdown = React.createClass({
componentDidUpdate: function() {
marked.setOptions({
highlight: function(code, lang) {
return hljs.highlightAuto(code, [lang]).value;
}
});
},
propTypes: {
markdown: React.PropTypes.string.isRequired
},
render: function() {
var html = marked(this.props.markdown);
return (
<div dangerouslySetInnerHTML={{__html: html}}></div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById("content")
);
テキストの入力部分のコンポーネント、 マークダウンの出力部分のコンポーネント、それらを統合するコンポーネントの3つに分割されています。
マークダウンのパースはmarkedという関数で行っています。
このmarked関数のオプションをcomponentDidUpdateのところでhighlight.jsを使うよう設定しています。
オプションの設定方法についてはhighlight.jsのREADMEにかいてあります。
dangerouslySetInnerHTMLというのはxss対策でデータをサニタイズするプロパティです。
初めてエディタつくったのですが、ライブラリでパパっと出来てしまうのですね〜(:3」∠)
先日、ES6を勉強したので書き換えてみました。propsTypeの対応方法はよくわからなかったので省略してしまいましたw
/**
*
* Editor
*
*/
import React from 'react';
import ReactDOM from 'react-dom';
export default class Editor extends React.Component{
constructor(props) {
super(props);
this.state = {
markdown: ''
};
this.updateMarkdown = this.updateMarkdown.bind(this);
}
updateMarkdown(markdown) {
this.setState({
markdown: markdown
});
}
render() {
return (
<div>
<TextInput onChange={this.updateMarkdown}/>
<Markdown markdown={this.state.markdown}/>
</div>
);
}
};
class TextInput extends React.Component{
constructor(props) {
super(props);
this._onChange = this._onChange.bind(this);
}
_onChange(e) {
this.props.onChange(e.target.value);
}
render() {
return (
<textarea onChange={this._onChange}></textarea>
);
}
};
class Markdown extends React.Component{
constructor(props) {
super(props);
}
componentDidUpdate() {
marked.setOptions({
highlight: function(code, lang) {
return hljs.highlightAuto(code, [lang]).value;
}
});
}
render() {
var html = marked(this.props.markdown);
return (
<div dangerouslySetInnerHTML={{__html: html}}></div>
);
}
};
関連書籍