ianstormtaylor / slate

A completely customizable framework for building rich text editors. (Currently in beta.)
http://slatejs.org
MIT License
29.71k stars 3.24k forks source link

Looks like splitBlock() duplicates void inlines #941

Closed ezakto closed 7 years ago

ezakto commented 7 years ago

Do you want to request a feature or report a bug?

Bug

What's the current behavior?

Looks like when a selection has an edge in a inline void (like an image) and you splitBlock() there, the inline gets duplicated in the second block.

  1. Go to http://slatejs.org/#/emojis
  2. Move the editor caret to the last position before the smile
  3. Press enter. You'll get two smiles: slate1

Another:

  1. Go to http://slatejs.org/#/emojis
  2. Move the editor caret to the beginning of the burger line
  3. If you press start start, works fine. But if you type something an then press start, you'll get two delicious burgers: slate2

What's the expected behavior?

Shouldn't duplicate the void node. I haven't been able to debug this properly, but, if it helps, I found a workaround that might give some hints.

In the onKeyDownEnter method of the core plugin, we're verifying if the parent is void to take care of that, and we're just calling splitBlock() if it's not. Well, I tweaked that method to check if the next sibling is a img (could probably work for any void inline) and if that's the case, I'm inserting a space, splitting before that space, and removing it after that. And that makes it work. Something like this:

function onKeyDownEnter(e, data, state) {
  const { document, startKey } = state
  const hasVoidParent = document.hasVoidParent(startKey)

  // For void nodes, we don't want to split. Instead we just move to the start
  // of the next text node if one exists.
  if (hasVoidParent) {
    const text = document.getNextText(startKey)
    if (!text) return
    return state
      .transform()
      .collapseToStartOf(text)
      .apply()
  }

  const sibling = document.getNextSibling(startKey)

  if (sibling && sibling.kind === 'inline' && sibling.isVoid) {
    return state
      .transform()
      .insertText(' ')
      .move(-1)
      .splitBlock()
      .deleteForward(1)
      .apply()
  }

  return state
    .transform()
    .splitBlock()
    .apply()
}
ianstormtaylor commented 7 years ago

I think this is solved now!