Reputation: 387
Using Draft-JS, I've a chunk of code which will replace a word with an Entity on clicking 'Return' so user types in 'i love eggplant' then 'Return' they should see 'i love :eggplant:'
After the entity has been added, when I do an undo (ctrl z
, nothing fancy), it removes all of my sentence rather than just the entity. Based on what I've read about Draft-JS I would have expected it to revert to 'i love eggplant' which is the desired effect.
This code is very stripped down from the full code for readability, yet demos the point correctly
const {
Editor,
Modifier,
EditorState,
RichUtils,
CompositeDecorator,
EditorChangeType,
getDefaultKeyBinding,
} = Draft;
class Example extends React.Component {
constructor(props){
super(props)
const compositeDecorator = new CompositeDecorator([
{ strategy: getEntityStrategy('LINK'), component: LinkComponent },
]);
this.state = { editorState: EditorState.createEmpty(compositeDecorator) };
this.onChange = (editorState) => { this.setState({editorState}) };
this.handleReturn = this.handleReturn.bind(this);
}
handleReturn(e, editorState) {
e.preventDefault();
const { start, end, text } = getFullWordWithCoordinates(editorState);
const contentState = editorState.getCurrentContent();
const selectionState = editorState.getSelection();
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{ status: 'complete' }
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newContentState = Modifier.replaceText(contentState,
selectionState.merge({ anchorOffset: start, focusOffset: end }),
`:${text}:`,
null,
entityKey);
const newEditorState = EditorState.set(editorState, { currentContent: newContentState });
this.setState({ editorState: EditorState.moveFocusToEnd(newEditorState) });
return 'handled';
}
render(){
return (
<div style={{border: '1px solid black', padding: '8px'}}>
<Editor
handleReturn={this.handleReturn}
editorState={this.state.editorState}
onChange={this.onChange} />
</div>
)
}
}
function getFullWordWithCoordinates(editorState) {
const selectionState = editorState.getSelection();
const anchorKey = selectionState.getAnchorKey();
const currentContent = editorState.getCurrentContent();
const currentContentBlock = currentContent.getBlockForKey(anchorKey);
const start = selectionState.getStartOffset();
const end = selectionState.getEndOffset();
const blockText = currentContentBlock.getText();
let wholeWordStart = start;
let wholeWordEnd = end;
while (blockText.charAt(wholeWordStart - 1) !== ' ' && wholeWordStart > 0) {
wholeWordStart--;
}
while (blockText.charAt(wholeWordEnd) !== ' ' && wholeWordEnd < blockText.length) {
wholeWordEnd++;
}
return {
text: currentContentBlock.getText().slice(wholeWordStart, wholeWordEnd),
start: wholeWordStart,
end: wholeWordEnd,
};
}
function getEntityStrategy(type) {
return function(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
if (entityKey === null) {
return false;
}
return contentState.getEntity(entityKey).getType() === type;
},
callback
);
};
}
const LinkComponent = (props) => (<span style={{ background: 'red'}}>{props.children}</span>)
ReactDOM.render(
<Example />,
document.getElementById('target')
);
Upvotes: 0
Views: 2771
Reputation: 3311
the EditorState.push()
API says Based on the changeType, this ContentState may be regarded as a boundary state for undo/redo behavior.
I found the user defined changeType
will make a boundary, so just make a push:
change
const newEditorState = EditorState.set(editorState, { currentContent: newContentState });
with
const newEditorState = EditorState.push(editorState, newContentState ,"addentity");
Upvotes: 2