Creating a rich text editor - Part 2 - Text Styling
6 mins read
In the first part of the tutorial, we built the simplest form of the text editor where we can just type some text. In this part, we will be adding features to add basic text styling to the characters.
We will start by adding a functionality where one can add bold, italic and underline styles to selected text by pressing the stand keyboard shortcuts for those, i.e
Mac | Windows/Linux | Action |
---|---|---|
CMD+B | CTRL+B | Bold |
CMD+I | CTRL+I | Italic |
CMD+U | CTRL+U | Uunderline |
We are using draft-js-plugins
editor which allows us to build various plugins and pass them to the Editor
component. We can choose to either create a single plugin with all the functionalities in it or build multiple plugins with single functionality in each. For this tutorial, we will be following the latter approach.
There are various handlers and properties that the Editor
component can be passed for different kind of events. You can read the documentation here. draft-js-editor
helps us in using these handlers in each of the plugins that we create.
The way Draft-js
works is that instead of directly relying on browser’s contenteditable API, it treats each keypress as an input. Then it analyzes the key combination and based on that it generates an output and renders it in the contenteditable
. Internally, on each keypress, a string signal is generated which is then analyzed and output is rendered.
Now that we have understood how draft-js works, let’s start by creating a plugin that handles keyboard shortcuts mentioned above. Draft-js
has a utility method that detects common key combinations and emits desired signal strings. For example, when CMD+B is pressed on Mac, it emits string - bold
. So we will be using this utility method to add basic styling to text using keyboard.
Simplest draft-plugin-plugin
is just on object with all or some of the handlers that you want to use. We will be using the keyBindingFn
handler of Editor
to add B, I, U styling. Let’s start by creating a plugins
directory inside src
. Now create a file called basicTextStylePlugin.js
inside plugins
and add the following code –
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {
getDefaultKeyBinding,
RichUtils,
} from 'draft-js';
const basicTextStylePlugin = {
keyBindingFn(event) {
return getDefaultKeyBinding(event);
},
handleKeyCommand(command, { getEditorState, setEditorState }) {
const editorState = getEditorState();
const newEditorState = RichUtils.handleKeyCommand(
editorState, command
);
if (newEditorState) {
setEditorState(newEditorState);
return 'handled';
}
return 'not-handled';
}
};
export default basicTextStylePlugin;
Above code creates the simplest form of draft-js-plugins
where it is an object with the handlers to use, here, the handlers are keyBindingFn
and handleKeyCommand
. In each of the method of draft-js-plugins
, besides the default argument passed by draft-js
s Editor
component, a second argument is also passed which is an object that has the getEditorState
and setEditorState
methods which we are using in handleKeyCommand
to modify the content.
keyBindingFn
takes the keyboard inputevent
object and passes it togetDefaultKeyBinding
utility function ofdraft-js
, and returns the resultant string. In this case, when one presses CMD+B, it returnsbold
.handleKeyCommand
is called whenever a string signal is emitted. In this case, when we returnbold
string fromkeyBindingFn
, it is passed ontohandleKeyCommand
. In this method, we can analyse the command string and perform actions based on that. Since we are just using internal command ofdraft-js
, we won’t be doing any extra checks right now. We will useRichUtils
object ofdraft-js
which takes as input, theeditorState
and the stringcommand
and returns the modifiededitorState
. We use thesetEditorState
method to update our content after modification. We return ahandled
string if we have modified the contents on our own and don’t wantdraft-js
to apply the default behaviour. Otherwise we returnnot-handled
so that other plugins, if any, can do their modifications.
Now, let us use this basicTextStylePlugin
in our MyEditor
component of src/App.js
. Before proceeding, lets change the name of our files to reflect the names of the components they have. Change the name of src/App.js
to src/MyEditor.js
, src/App.css
to src/MyEditor.css
. Let’s also updated the file refernces. In src/MyEditor.js
update the import of App.css
to MyEditor.css
and update index.js
to –
1
2
3
4
5
6
7
8
9
import React from 'react';
import ReactDOM from 'react-dom';
import MyEditor from './MyEditor';
import './index.css';
ReactDOM.render(
<MyEditor />,
document.getElementById('root')
);
At this point, your directory structure will look like this –
Now, let’s start using the newly created plugin. Open src/MyEditor.js
and modify the contents (modified lines are just below the inline comments) –
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
51
52
53
54
55
56
import 'draft-js/dist/Draft.css';
import './MyEditor.css';
import React from 'react';
import { EditorState } from 'draft-js';
import Editor from 'draft-js-plugins-editor';
/* Import the `basicTextStylePlugin` */
import basicTextStylePlugin from './plugins/basicTextStylePlugin';
class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
/* Create an array of plugins to be passed to `Editor` */
this.plugins = [
basicTextStylePlugin,
];
}
componentDidMount() {
this.focus();
}
onChange = (editorState) => {
this.setState({
editorState,
});
}
focus = () => {
this.editor.focus();
}
render() {
const { editorState } = this.state;
return (
<div className="editor" onClick={this.focus}>
<Editor
editorState={editorState}
onChange={this.onChange}
plugins={this.plugins} // Pass the plugins to the Editor
ref={(element) => { this.editor = element; }}
placeholder="Tell your story"
spellCheck
/>
</div>
);
}
}
export default MyEditor;
Now, you can open your dev server @ http://localhost:3000
where you will be able to select some typed text and press CMD+B to make the selected text bold.
At this point or even at the end of previous tutorial, you may have gotten a warning message in the console like this Warning: Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead.
. This is because we are using the latest version of React
from which PropTypes
have been removed to another package called prop-types
and other libraries (draft-js
and draft-js-plugins
) are using the old PropTypes
. This is nothing to be worried about.
This completes the 2nd part of the tutorial. In next part, we will learn about entities
and implement adding a link to the selected text.
You can get the source code from this GitHub repo . The code for this tutorial can be run from the part2
tag. After cloning the repo, you can do
git checkout part2
List of tutorials in this series –
- Part 1 - Barebones Editor
- Part 2 - Text Styling
- Part 3 - Entities and decorators