Draft-js highlight text

This is the sixth in an ongoing series of posts about draft-js.

  1. draft-js pieces
  2. draft-js EditorState
  3. draft-js ContentState
  4. draft-js ContentBlock
  5. draft-js SelectionState
  6. draft-js highlight text
  7. draft-js styling a ContentBlock

In this post I’ll look at adding some basic but super extensible functionality to a selection.  This is the first thing we tackled so that’s the order I’m going in :).

If you are using a wrapper like react-rte they you’ll have a toolbar that has some functionality like Bold, Italic, BulletList, NumberList, but not a lot.  You’ll quickly find some text decoration you need that is not there.  For us, we needed highlighting.

So the workflow would be

  • highlight some text with your mouse or keyboard
  • click a button
  • maybe get a popup with a color picker
  • the text background-color: myColor

Based on the first bullet point there, you can guess that we are going to grab a SelectionState.  But how?  Well,  it’s odd, but it seems that draft-js only exposes an onChange event.  I’m sure you could hack other events in and I may do that later but for now I just went with onChange.

The first thing you’ll want to do is make sure you are dealing with a selection so

isSelection = (editorState) => {
    const selection = editorState.getSelection();
    const start = selection.getStartOffset();
    const end = selection.getEndOffset();
    return start !== end;
};

onChange(editorState) {
  if (!this.isSelection(editorState)) {
    return;
  }
}

Now at least we know we are dealing with a selection of one or more characters.  The next step is to apply the modification.

import {RichUtils} from 'draft-js'

onChange(editorState) {
  if (!this.isSelection(editorState)) {
    return;
  }
  editorState = RichUtils.toggleInlineStyle(editorState, 'HIGHLIGHT');
}

RichUtils are/is a collection of helper functions provided by draft-js.  First toggleInlineStyles will look at the EditorState.getSelection() and look for HIGLIGHT and add it if it’s not there and remove it if it is.  yes.  toggle it.  Then it will return a new editorState.

But what the hell is HIGHTLIGHT?  Well you didn’t think it was gonna be that easy did you?  When you declare your Editor component you can pass it a collection of string/cssObject pairs using the property customStyleMap.

const styleMap = {
  'HIGHLIGHT': {
    backgroundColor: 'lightgreen'
   }
};
<Editor
  customStyleMap={styleMap} 
  editorState={this.state.editorState}
  ... /> 

now when you do

editorState = RichUtils.toggleInlineStyle(editorState, 'HIGHLIGHT');

you will be referencing that piece of reactified css.  What we did of course was to define colorHighlights so greenHighLight, blueHighLight etc so we could have multiple colors. The Editor’s styleMap has a number of default styles, Bold, Italic, Underline and Code.  You can use these the same way with toggleInlineStyles and you can override them with customStyleMap if you’d like them to look differently.

It seems like you might want to be able to do this even more dynamically so the consumer could click a color picker and use that.  I don’t know how to do that, but if I figure it out I’ll post about it.

But wait, you’re not done yet.  You have just mutated EditorState and need to locally persist that ( at least ) .  It depends on how you are storing state in your application, if you are storing it in redux then execute an action and pass the new EditorState in.  If you are handling it locally then do a setState({value: editorState}).  So the whole piece will look like this

isSelection = (editorState) => {
    const selection = editorState.getSelection();
    const start = selection.getStartOffset();
    const end = selection.getEndOffset();
    return start !== end;
};

onChange(editorState) {
  if (!this.isSelection(editorState)) {
    return;
  }
  editorState = RichUtils.toggleInlineStyle(editorState, 'HIGHLIGHT');
  // using concise obj prop notation short for {editorState:editorState}
  this.setState({editorState}); 
}
const styleMap = {
  'HIGHLIGHT': {
    backgroundColor: 'lightgreen'
   }
};

<Editor
  customStyleMap={styleMap} 
  editorState={this.state.editorState}
  ... /> 

That’s all there is too it.  You may wonder how to hook this up to a button elsewhere on the screen. Ultimately you are just acting on state which holds the EditorState.  The EditorState in the onChange event has the current SelectionState in it.  You can update state then act on that SelectionState elsewhere in your app.  When you pass it back to your app state it will render the subsequent changes.

 

Advertisements
Draft-js highlight text

7 thoughts on “Draft-js highlight text

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s