Draft.js
Gore Wang , 2017 @ 線上 React 讀書會
Email:sunrise91.t3@gmail.com
簡介
資料結構
3種裝飾
Agenda
簡介
優點
- 弭平各家瀏覽器實踐Contenteditable的差異性
- 在Contenteditable中更自由實現控制HTML
缺點
- 專案必定得連帶引入 Immutable.js
- 他只能在React上用
資料結構
// 簡述
import { EditorState, Editor } from 'draft-js';
constructor() {
this.state = {
editorState: EditorState.createEmpty()
};
}
render() {
return (
<Editor editorState={ this.state.editorState }/>
)
}
EditorState
// 實際上是immutable的形式,以下只是方便解說羅列
EditorState: {
currentContent: ...
selection: ...
decorator: ...
lastChangeType:"insert-characters"
directionMap: ...
allowUndo: true
redoStack: ...
undoStack: ...
...
}
ContentState
// 實際上是immutable的形式,以下只是方便解說羅列
ContentState: {
blockMap: ...
entityMap: ...
...
}
都~是更新State
我是說所有的事情...
所以才需要 immutable
3 Styling System
- Inline Style
- Block
- Decorator
2 Helper
Inline Styles
const styleMap = {
HEIGH_LIGHT: {
backgroundColor: 'green'
}
}
<Editor customStyleMap={ styleMap }/>
const newEditorState = RichUtils.toggleInlineStyle(editorState, 'HEIGH_LIGHT')
currentContent:{
blockMap:{
blockId: {
characterList:[
{
entity:null,
style:[]
},
{
entity:null,
style:["HEIGH_LIGHT"]
}
]
}
}
}
會在characterList替所選的每一個字註記style type
Block Styling
註冊Block Type:
import Immutable from 'immutable';
const BlockRenderMap = Immutable.Map({
'superTitleBlock': {
element: 'h1',
wrapper: <BlockWapperTest/>
}
})
function myBlockStyleFn(contentBlock) {
const type = contentBlock.getType();
switch (type) {
case 'superTitleBlock':
return 'super__title' // => <h1 class="super__title"...
}
}
<Editor
blockRenderMap={ extendedBlockRenderMap }
blockStyleFn={ myBlockStyleFn } />
contentBlock: {
blocks:[
{
text: "block styling測試",
type: "superTitleBlock",
...
}
]
}
class BlockWapperTest extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className='MyCustomBlock'>
{ this.props.children }
</div>
);
}
}
Decorator
- Regex
- Entity
- ...
Strategy:
倚靠指定「匹配範圍」傳入component實踐styling
Decorator Strategy (regex)
const compositeDecorator = new CompositeDecorator([
{
strategy: function(contentBlock, callback, contentState) {
const text = contentBlock.getText();
const hashReg = /\#[\w\u0590-\u05ff]+/g;
let matchArr,
start;
while ((matchArr = hashReg.exec(text)) !== null) {
start = matchArr.index;
callback(start, start + matchArr[0].length);
}
},
component: HashtagSpan,
}
]);
this.state = {
editorState: EditorState.createEmpty(compositeDecorator)
};
Entity
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{href: 'http://www.zombo.com'}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const contentStateWithLink = Modifier.applyEntity(
contentState,
selectionState,
entityKey
);
主要用在創建有帶異動性資料的區塊
(e.g. link url, image url ...)
currentContent:{
blockMap:{
blockId: {
characterList:[
{
entity:null,
style:[]
},
{
entity:1, // entity id
style:[]
},
...
]
}
}
}
Decorator Strategy (Entity)
const compositeDecorator = new CompositeDecorator([
{
strategy: function(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (entityKey !== null
&&
contentState.getEntity(entityKey).getType() == 'LINK')
},
callback)
},
component: TestDecoratorWapper
},
]);
Key Bindings
<Editor
keyBindingFn={ KeyBindingMap }
handleKeyCommand={ this.handleKeyCommand }/>
import { getDefaultKeyBinding, KeyBindingUtil } from 'draft-js';
const {hasCommandModifier} = KeyBindingUtil;
function keyBindingFn(e) {
const hasCommand = hasCommandModifier(e);
switch (e.keyCode) {
case ( hasCommand && 83):
return 'myeditor-save';
default:
return getDefaultKeyBinding(e);
}
}
function handleKeyCommand(command) => {
switch (command) {
case 'myeditor-save':
console.log('saved');
return 'handled'
default:
return 'not-handled'
}
};
坑
- 使用convertToRaw不只是toJS而已,儲存形式也會跟著改變...
- DraftJS Plugins beta 2.0.0,有些plugin支援先升了但沒寫...
- (待更新...)
Thanks~!
Draft.js
By Gore Wang
Draft.js
用React實作自由度超高的Rich Text Editor
- 2,097