The problem

Imagine you have a nested list(ordered or unordered). When you have finished your work with current depth level, you need to go one level up. The common way to do it is to leave the item blank and hit Return button. The depth level would be decreased and you can continue your work without even thinking about pressing specific key combo. And if the the cursor is on the first depth level the editor should close the list.

Google Docs

By default DraftJS implements different logics — when you hit Return on a blank list item a new item is created.

DraftJS default behaviour

To improve the UX of your app you can use this plugin or follow the steps below.

Solution

The algorithm is pretty simple: when user has pressed Return we need to check if current list item is empty. If so — we need to check the depth level. If it is greater than 0 we need to decrease it. Otherwise we need to close the list. The depth level can’t be less than 0. As always, things start to get complicated when it goes down to DraftJS and it’s documentation.

Since we need to alter the behaviour of Return key press, DraftJS method handleReturn would be a great point to start! First of all, we need to get currently selected block(if there is any) to work with. To do it, we need to get current states of selection and content and select a block where the selection starts.

Once the block is selected we need to check if the block is a list item and if this item has no text. Let’s implement a function to do this.

Now we can detect the exact case when we need to alter the default behaviour. Let’s now combine everything we have so far.

So far so good! Now let’s implement handleReturnForListItem method according to the described algorithm.

Changing block’s depth level turned out to be pretty simple. Since DraftJS uses Immutable.js internally, we can call set method of the block to get a copy of the object with updated property. Then we need to update the state of the editor accordingly.

Note that EditorState from DraftJS is used here — not a locally stored editorState . Also note that adjust-depth change type is used here. DraftJS uses change-types to determine if current change may be regarded as a boundary state for undo/redo behaviour.

We’re almost done now! The only part left to be written is the logics to close the list if depth level is 0.

Method tryToRemoveBlockStyle from RichUtils module allows us to safely change block type to unstyled . This means that our list would be closed and the last item in this list would be the previous one.

We applied common pattern to DraftJS editor. Good work!

If you’ve read this post till this moment you may also want to check my other post and apply this enhancement as well.