Hey yo, hello all of you
It’s me again, Tan was handsome from a young age or shared with everyone every month here.
Today, I will share with you about a package that has been a headache for me for a very long time.
Well, I still have a headache because of it. Yes, it is Draft js – a row from the big man Facebook.
overview
Draft.js is a framework for building text editors in React, powered by an immutable and abstracting model based on differences between browsers.
Draft.js lets you create any type of text input, whether you just want to support a few on-line text styles or build a complex text editor for writing long form articles.
Draft.js was introduced at React.js Conf in February 2016.
Setting
To install Draft.js, you dare to do the following combo:
1 2 3 4 | <span class="token function">npm</span> <span class="token function">install</span> draft-js <span class="token comment"># hoặc dùng yarn</span> <span class="token function">yarn</span> <span class="token function">add</span> draft-js |
Use
In terms of usage, there are 2 approaches that follow the class component or function component . In this article, I will use the function component offline.
Simply because I am lazy to play with this.abc, this.xyz, this.blabla, … only. And here is the basic syntax to use it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token comment">// file Editor.js</span> <span class="token keyword">import</span> React <span class="token punctuation">,</span> <span class="token punctuation">{</span> useRef <span class="token punctuation">,</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span> <span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> Editor <span class="token punctuation">,</span> EditorState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'draft-js'</span> <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">MyEditor</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> editorRef <span class="token operator">=</span> <span class="token function">useRef</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">[</span> editorState <span class="token punctuation">,</span> setEditorState <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> EditorState <span class="token punctuation">.</span> <span class="token function">createEmpty</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">focus</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> editorRef <span class="token punctuation">.</span> current <span class="token punctuation">.</span> <span class="token function">focus</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"custom-editor"</span> onClick <span class="token operator">=</span> <span class="token punctuation">{</span> focus <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token comment">// hàm focus ở đây có tác dụng khi chúng ta click </span> <span class="token operator"><</span> Editor <span class="token comment">// vào thẻ div chứa editor thì sẽ focus vào editor </span> ref <span class="token operator">=</span> <span class="token punctuation">{</span> editorRef <span class="token punctuation">}</span> <span class="token comment">// để chúng ta cào phím lun :)</span> editorState <span class="token operator">=</span> <span class="token punctuation">{</span> editorState <span class="token punctuation">}</span> onChange <span class="token operator">=</span> <span class="token punctuation">{</span> setEditorState <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> MyEditor <span class="token punctuation">;</span> |
And here is the css code I adjusted the editor to look nice, in addition you can use the draft.js css directly by putting its draft-js/dist/Draft.css
file directly into the js file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token selector">.custom-editor</span> <span class="token punctuation">{</span> <span class="token property">padding</span> <span class="token punctuation">:</span> 24px 200px <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.DraftEditor-root</span> <span class="token punctuation">{</span> <span class="token property">min-height</span> <span class="token punctuation">:</span> 200px <span class="token punctuation">;</span> <span class="token property">border</span> <span class="token punctuation">:</span> solid 1px #bbb <span class="token punctuation">;</span> <span class="token property">box-sizing</span> <span class="token punctuation">:</span> border-box <span class="token punctuation">;</span> <span class="token property">border</span> <span class="token punctuation">:</span> 1px solid #ddd <span class="token punctuation">;</span> <span class="token property">cursor</span> <span class="token punctuation">:</span> text <span class="token punctuation">;</span> <span class="token property">padding</span> <span class="token punctuation">:</span> 16px <span class="token punctuation">;</span> <span class="token property">border-radius</span> <span class="token punctuation">:</span> 2px <span class="token punctuation">;</span> <span class="token property">margin-bottom</span> <span class="token punctuation">:</span> 2em <span class="token punctuation">;</span> <span class="token property">box-shadow</span> <span class="token punctuation">:</span> inset 0px 1px 8px -3px #ABABAB <span class="token punctuation">;</span> <span class="token property">background</span> <span class="token punctuation">:</span> #fefefe <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
And here are the results
A few basic features
RichUtils
RichUtils contains information about the main commands available to the editor, such as Cmd + B (bold), Cmd + I (italic), …
The steps to use it would look like this:
- import RichUtils
- When you click on the inline style button, use the toggleInlineStyle (editorState, style) function to create a new editorState.
- Update the latest editor status to editorState.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token comment">// Ví dụ với bold style</span> <span class="token keyword">import</span> <span class="token punctuation">{</span> RichUtils <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'draft-js'</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> onBoldClick <span class="token operator">=</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">{</span> e <span class="token punctuation">.</span> <span class="token function">preventDefault</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Mình dùng preventDefault() để giữ con trỏ chuột vẫn còn ở trong editor nhé các bạn</span> <span class="token function">setEditorState</span> <span class="token punctuation">(</span> RichUtils <span class="token punctuation">.</span> <span class="token function">toggleInlineStyle</span> <span class="token punctuation">(</span> editorState <span class="token punctuation">,</span> <span class="token string">'BOLD'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// ở file render Editor</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> div className <span class="token operator">=</span> <span class="token string">"custom-editor"</span> onClick <span class="token operator">=</span> <span class="token punctuation">{</span> focus <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token operator"><</span> span onMouseDown <span class="token operator">=</span> <span class="token punctuation">{</span> onBoldClick <span class="token punctuation">}</span> <span class="token operator">></span> Click Bold <span class="token operator"><</span> <span class="token operator">/</span> span <span class="token operator">></span> <span class="token operator"><</span> Editor ref <span class="token operator">=</span> <span class="token punctuation">{</span> editorRef <span class="token punctuation">}</span> editorState <span class="token operator">=</span> <span class="token punctuation">{</span> editorState <span class="token punctuation">}</span> onChange <span class="token operator">=</span> <span class="token punctuation">{</span> setEditorState <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Demo goods
AtomicBlockUtils
AtomicBlockUtils is a static set of utility functions for editing atomic blocks. This atomic block can be image, audio or even video.
In each case, these methods accept EditorState objects with the associated parameters and return new EditorState objects to update into the editor.
Example of inserting images using AtomicBlockUtils.insertAtomicBlock
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <span class="token comment">// hàm addImage</span> <span class="token keyword">const</span> <span class="token function-variable function">addImage</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> e <span class="token punctuation">.</span> <span class="token function">preventDefault</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> contentState <span class="token operator">=</span> editorState <span class="token punctuation">.</span> <span class="token function">getCurrentContent</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> contentStateWithEntity <span class="token operator">=</span> contentState <span class="token punctuation">.</span> <span class="token function">createEntity</span> <span class="token punctuation">(</span> <span class="token string">'image'</span> <span class="token punctuation">,</span> <span class="token string">'IMMUTABLE'</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> src <span class="token operator">:</span> imgUrl <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> entityKey <span class="token operator">=</span> contentStateWithEntity <span class="token punctuation">.</span> <span class="token function">getLastCreatedEntityKey</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> newEditorState <span class="token operator">=</span> EditorState <span class="token punctuation">.</span> <span class="token function">set</span> <span class="token punctuation">(</span> editorState <span class="token punctuation">,</span> <span class="token punctuation">{</span> currentContent <span class="token operator">:</span> contentStateWithEntity <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">setEditorState</span> <span class="token punctuation">(</span> AtomicBlockUtils <span class="token punctuation">.</span> <span class="token function">insertAtomicBlock</span> <span class="token punctuation">(</span> newEditorState <span class="token punctuation">,</span> entityKey <span class="token punctuation">,</span> <span class="token string">' '</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// Tiến hành render Image component trong editor</span> <span class="token keyword">const</span> <span class="token function-variable function">Image</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">{</span> contentState <span class="token punctuation">,</span> block <span class="token punctuation">}</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> entity <span class="token operator">=</span> contentState <span class="token punctuation">.</span> <span class="token function">getEntity</span> <span class="token punctuation">(</span> block <span class="token punctuation">.</span> <span class="token function">getEntityAt</span> <span class="token punctuation">(</span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> src <span class="token punctuation">}</span> <span class="token operator">=</span> entity <span class="token punctuation">.</span> <span class="token function">getData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> src <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator"><</span> img src <span class="token operator">=</span> <span class="token punctuation">{</span> src <span class="token punctuation">}</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">mediaBlockRenderer</span> <span class="token punctuation">(</span> <span class="token parameter">block</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> block <span class="token punctuation">.</span> <span class="token function">getType</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">'atomic'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> component <span class="token operator">:</span> Image <span class="token punctuation">,</span> editable <span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">null</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// editor component</span> <span class="token operator"><</span> Editor blockRendererFn <span class="token operator">=</span> <span class="token punctuation">{</span> mediaBlockRenderer <span class="token punctuation">}</span> <span class="token operator">...</span> <span class="token operator">/</span> <span class="token operator">></span> |
The steps will be:
- Get current content
- Create a new Entity with type
image
and mutability will beIMMUTABLE
- Get the newly created entity key and use
AtomicBlockUtils.insertAtomicBlock
to update the editor’s new state. - Add
blockRendererFn
in Editor component to render Image.
And here are the results of my demo test:
As you can see, it’s very i zì, isn’t it
There is a small minus point when inserting images in this way will appear two blank lines before and after inserting a feeling of loneliness when the night comes without bears.
Currently, I’m still trying to delete it, so anyone who knows the solution can comment so I can test it quickly
Decorators
The decorator concept is based on scanning the content of a certain ContentBlock to find the text scope matching the specified strategy , then rendering them with a specified React component .
You can use the CompositeDecorator class to determine your desired decorator behavior.
The following is an example of finding a link in my editor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <span class="token comment">// Định nghĩa hàm tìm kiếm link entity</span> <span class="token keyword">function</span> <span class="token function">findLinkEntities</span> <span class="token punctuation">(</span> <span class="token parameter">contentBlock <span class="token punctuation">,</span> callback <span class="token punctuation">,</span> contentState</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> contentBlock <span class="token punctuation">.</span> <span class="token function">findEntityRanges</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">character</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> entityKey <span class="token operator">=</span> character <span class="token punctuation">.</span> <span class="token function">getEntity</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> entityKey <span class="token operator">!==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> contentState <span class="token punctuation">.</span> <span class="token function">getEntity</span> <span class="token punctuation">(</span> entityKey <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getType</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">'LINK'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> callback <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Định nghĩa Link component sẽ được dùng nếu tìm thấy link</span> <span class="token keyword">const</span> <span class="token function-variable function">Link</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">{</span> contentState <span class="token punctuation">,</span> entityKey <span class="token punctuation">,</span> children <span class="token punctuation">}</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> url <span class="token punctuation">}</span> <span class="token operator">=</span> contentState <span class="token punctuation">.</span> <span class="token function">getEntity</span> <span class="token punctuation">(</span> entityKey <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span> a href <span class="token operator">=</span> <span class="token punctuation">{</span> url <span class="token punctuation">}</span> <span class="token operator">></span> <span class="token punctuation">{</span> children <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> a <span class="token operator">></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token comment">// Khởi tạo decorator và thêm vào editorState</span> <span class="token keyword">const</span> decorator <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CompositeDecorator</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> strategy <span class="token operator">:</span> findLinkEntities <span class="token punctuation">,</span> component <span class="token operator">:</span> Link <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">[</span> editorState <span class="token punctuation">,</span> setEditorState <span class="token punctuation">]</span> <span class="token operator">=</span> React <span class="token punctuation">.</span> <span class="token function">useState</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> EditorState <span class="token punctuation">.</span> <span class="token function">createEmpty</span> <span class="token punctuation">(</span> decorator <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Định nghĩa hàm addLink</span> <span class="token keyword">const</span> <span class="token function-variable function">addLink</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> e <span class="token punctuation">.</span> <span class="token function">preventDefault</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> contentState <span class="token operator">=</span> editorState <span class="token punctuation">.</span> <span class="token function">getCurrentContent</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> contentStateWithEntity <span class="token operator">=</span> contentState <span class="token punctuation">.</span> <span class="token function">createEntity</span> <span class="token punctuation">(</span> <span class="token string">'LINK'</span> <span class="token punctuation">,</span> <span class="token string">'MUTABLE'</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> url <span class="token operator">:</span> imgUrl <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> entityKey <span class="token operator">=</span> contentStateWithEntity <span class="token punctuation">.</span> <span class="token function">getLastCreatedEntityKey</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> newEditorState <span class="token operator">=</span> EditorState <span class="token punctuation">.</span> <span class="token function">set</span> <span class="token punctuation">(</span> editorState <span class="token punctuation">,</span> <span class="token punctuation">{</span> currentContent <span class="token operator">:</span> contentStateWithEntity <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">setEditorState</span> <span class="token punctuation">(</span> RichUtils <span class="token punctuation">.</span> <span class="token function">toggleLink</span> <span class="token punctuation">(</span> newEditorState <span class="token punctuation">,</span> newEditorState <span class="token punctuation">.</span> <span class="token function">getSelection</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> entityKey <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
And here is my demo
summary
So, together we learned a few basics of Draft Js.
Above I have briefly introduced Draft Js through the following jobs:
- How to install and use Draft js is simple.
- Use
RichUtils
for inline styles such as bold italic, underline, … - Use
AtomicBlockUtils
to insert atomic blocks (image, audio, video) into the editor. - Use
CompositeDecorator
to find and customize some content.
Hopefully through this article you can gradually approach and master this package
Let’s learn together to discover more interesting things that I have not yet fully introduced in the article