RFC: Composite entities
This is more of a brainstorming/discussion issue. Currently we can only apply a single entity per character. For simple things this is totally fine. But as soon as we want to handle more complex things like adding text when applying an entity, things get hairy.
Imagine we have the following situation: A user can highlight some text and assign some kind of highlight to it. This highlight should:
- Provide a link to an external page
- Append a link which links to an anchor on the same page (a collection of all used links)
Since decorators currently must not modify the text they are decorating (for obvious reasons), we cannot just append the link in the decorator. This has to happen when adding the entity, so we can modify the state correctly.
So here's my (high-level) idea: When adding an entity to a text we can specify a composite entity, which allows us to subdivide the range we want to annotate into sub-types. It could look something like this:
const currentContentState = editorState.getCurrentContent();
const targetRange = editorState.getSelection();
const metaData = { someId: 5 };
// just a dummy value, would be extracted from the selection/text or could
// be custom if we want to append text to the selection but decorate that specific piece of text
const rangeInfo = [[0,10], [11, 13]];
const entityKey = Entity.createComposite('MY_ENTITY', 'MUTABLE', rangeInfo, metaData);
const contentStateWithEntity = Modifier.applyEntity(
currentContentState,
targetRange,
entityKey
);
const newEditorState = Draft.EditorState.push(
editorState,
contentStateWithCitation,
'add-my-entity',
);The createComposite method would create a special composite entity, which then can be used by draft to figure out how to split up the decorated text into multiple children, before passing them to the decorator.
Our decorator could then look like this:
const myEntityDecorator = ({ children, entityKey }) => {
const [firstChild, secondChild] = children;
const { someId } = Entity.get(entityKey).getData();
return (
<span>
{firstChild}
<a href={`#${someId}`}>
{secondChild}
</a>
</span>
);
});I am aware that the above can be achieved in a slightly different way by adding two separate entities, but by doing that we lose the ability to make the them immutable (we could remove either one of them and the other one wouldn't be affected) and we would need two different entity types, even though it really only should be a single one.
Crazy?
Source: facebookarchive/draft-js