Cursor positioning problem using Entity
See original GitHub issueHello, it’s my first post here, so sorry in advance for any mistake.
I’m using Draft to create a message input component.
I’m using decorators to decorate “hashtags”, and Entity to render emojis.
I’m using entities for emojis in order to set the text “IMMUTABLE”, and be able to delete the whole text from the emoji at once, which is a pattern like this :emoji:
The problem is with the input focus when rendering the emojis. When the cursor is “in the front” of the emoji, it starts typing “behind” the emoji.
I’ve found that setting data-offset-key property into my emoji component improved a lot the control of the cursor,
DOM using data-offset-key: http://hnordt.d.pr/1ghat+
Problem with the cursor: http://hnordt.d.pr/1bk5d+
As you can see, when the cursor is in the next character after the emoji (Entity), it writes before the emoji. Only when navigating to the next character, it begins typing in the correct span.
Code when adding an emoji (emoji.colons is the emoji text ie “:emoji:”):
handleSelectEmoji(emoji) {
const { editorState } = this.state
const currentContent = editorState.getCurrentContent()
const selection = editorState.getSelection()
// Adding emoji and applying entity to it
const entityKey = Entity.create('EMOJI', 'IMMUTABLE', {emoji: emoji.id})
let newContent = Modifier.insertText(currentContent, selection, emoji.colons)
let newState = EditorState.createWithContent(newContent, compositeDecorator)
let selectionState = SelectionState.createEmpty(newContent.getFirstBlock().getKey())
let focusOffset = selection.getFocusOffset() + emoji.colons.length
selectionState = selectionState.merge({
anchorOffset: selection.getAnchorOffset(),
focusKey: newContent.getFirstBlock().getKey(),
focusOffset: focusOffset,
})
newContent = Modifier.applyEntity(newContent, selectionState, entityKey)
newState = EditorState.createWithContent(newContent, compositeDecorator)
//Setting cursor position after inserting emoji to content
selectionState = selectionState.merge({
anchorOffset: focusOffset,
focusKey: newContent.getFirstBlock().getKey(),
focusOffset: focusOffset,
})
newState = EditorState.forceSelection(newState, selectionState)
this.setState({ editorState: newState })
setTimeout(() => {
this.focus()
},0)
}
Decorator to transform emoji text in image:
const EmojiSpan = (props) => {
return (
<span data-offset-key={props.offsetKey} className="emoji-input-message">
<Emoji
sheetURL="img/sheet_apple_64.png"
emoji={{id:props.decoratedText.substring(1, props.decoratedText.length -1), skin: 1}}
size={22}
skin={1}
/>
</span>
)
}
function findEmojis(contentBlock, callback) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
Entity.get(entityKey).getType() === 'EMOJI'
);
},
callback
);
}
const compositeDecorator = new CompositeDecorator([
{
strategy: flowStrategy,
component: FlowSpan,
},
{
strategy: findEmojis,
component: EmojiSpan
}
])
Any ideas to solve that? Many thanks!
Issue Analytics
- State:
- Created 7 years ago
- Reactions:13
- Comments:6 (3 by maintainers)
While waiting for a permanent fix for this issue, Here is what I implemented
And then, the decorator strategy function:
notice that I’m calling the callback here with
end - 1
. The reason for this is because I’m attaching a thin space character (\u+200A
) to the entity data when creating the emoji entity. This will make it possible for the cursor to be able to move in front of and behind the emoji. For those who mind, here is how I render the emoji entity (using emoji-mart)@stopachka This sounds like the inline-block problem we discussed this week. I don’t think we know of any good solution currently. On Facebook we implement emojis by rendering a wide space and adding the emoji image as a background image.