Decorators
Inline and block styles aren't the only kind of rich styling that we might want to add to our editor. The Facebook comment input, for example, provides blue background highlights for mentions and hashtags.
To support flexibility for custom rich text, Draft provides a "decorator" system. The tweet example offers a live example of decorators in action.
CompositeDecorator
The decorator concept is based on scanning the contents of a given ContentBlock for ranges of text that match a defined strategy, then rendering them with a specified React component.
You can use the CompositeDecorator
class to define your desired
decorator behavior. This class allows you to supply multiple DraftDecorator
objects, and will search through a block of text with each strategy in turn.
Decorators are stored within the EditorState
record. When creating a new
EditorState
object, e.g. via EditorState.createEmpty()
, a decorator may
optionally be provided.
Under the hood
When contents change in a Draft editor, the resulting
EditorState
object will evaluate the newContentState
with its decorator, and identify ranges to be decorated. A complete tree of blocks, decorators, and inline styles is formed at this time, and serves as the basis for our rendered output.In this way, we always ensure that as contents change, rendered decorations are in sync with our
EditorState
.
In the "Tweet" editor example, for instance, we use a CompositeDecorator
that
searches for @-handle strings as well as hashtag strings:
This composite decorator will first scan a given block of text for @-handle matches, then for hashtag matches.
The strategy functions execute the provided callback with the start
and
end
values of the matching range of text.
Decorator Components
For your decorated ranges of text, you must define a React component to use
to render them. These tend to be plain span
elements with CSS classes or
styles applied to them.
In our current example, the CompositeDecorator
object names HandleSpan
and
HashtagSpan
as the components to use for decoration. These are basic
stateless components:
The Decorator Component will receive various pieces of metadata in props
,
including a copy of the contentState
, the entityKey
if there is one, and the
blockKey
. For a full list of props supplied to a Decorator Component see the
DraftDecoratorComponentProps type.
Note that props.children
is passed through to the rendered output. This is
done to ensure that the text is rendered within the decorated span
.
You can use the same approach for links, as demonstrated in our link example.
Beyond CompositeDecorator
The decorator object supplied to an EditorState
need only match the expectations
of the
DraftDecoratorType
Flow type definition, which means that you can create any decorator classes
you wish, as long as they match the expected type -- you are not bound by
CompositeDecorator
.
Setting new decorators
Further, it is acceptable to set a new decorator
value on the EditorState
on the fly, during normal state propagation, through immutable means.
This means that during your app workflow, if your decorator becomes invalid or
requires a modification, you can create a new decorator object (or use
null
to remove all decorations) and EditorState.set()
to make use of the new
decorator setting.
For example, if for some reason we wished to disable the creation of @-handle decorations while the user interacts with the editor, it would be fine to do the following:
The ContentState
for this editorState
will be re-evaluated with the new
decorator, and @-handle decorations will no longer be present in the next
render pass.
Again, this remains memory-efficient due to data persistence across immutable objects.