'Add paragraph after image within a slate-react text editor

I am currently working on rich text editor based on slatejs. I need to implement possibility to insert paragraph right after an image, when image is focused. Now when image has focus and I press Enter button - nothing happend. It should insert new empty paragraph right after the image.

Same behavior in example https://www.slatejs.org/examples/images

Any help appreciated



Solution 1:[1]

If you are selecting a void node (Image node), pressing enter won't add a new line by default. The most upvoted answer adds a new line only on image insert, which doesn't address the question.

Here's a plugin on how to give the editor your desired behavior.

import { Editor, Node, Path, Range, Transforms } from 'slate'

export const withCorrectVoidBehavior = editor => {
  const { deleteBackward, insertBreak } = editor

  // if current selection is void node, insert a default node below
  editor.insertBreak = () => {
    if (!editor.selection || !Range.isCollapsed(editor.selection)) {
      return insertBreak()
    }

    const selectedNodePath = Path.parent(editor.selection.anchor.path)
    const selectedNode = Node.get(editor, selectedNodePath)
    if (Editor.isVoid(editor, selectedNode)) {
      Editor.insertNode(editor, {
        type: 'paragraph',
        children: [{ text: '' }],
      })
      return
    }

    insertBreak()
  }
    
  // if prev node is a void node, remove the current node and select the void node
  editor.deleteBackward = unit => {
    if (
      !editor.selection ||
      !Range.isCollapsed(editor.selection) ||
      editor.selection.anchor.offset !== 0
    ) {
      return deleteBackward(unit)
    }

    const parentPath = Path.parent(editor.selection.anchor.path)
    const parentNode = Node.get(editor, parentPath)
    const parentIsEmpty = Node.string(parentNode).length === 0

    if (parentIsEmpty && Path.hasPrevious(parentPath)) {
      const prevNodePath = Path.previous(parentPath)
      const prevNode = Node.get(editor, prevNodePath)
      if (Editor.isVoid(editor, prevNode)) {
        return Transforms.removeNodes(editor)
      }
    }

    deleteBackward(unit)
  }

  return editor
}

We override the insertBreak behavior (which gets called on carrige return) and insert a blank line instead by calling Editor.insertNode(editor, blankNode) if the selected node is void.

We also override the deleteBackward behavior. Without the plugin, deleting an empty line right after a void node will delete the node too! Now, instead of deleting the node before, we delete the blank line and select the node before.

To use this plugin, you would do something like:

const editor = useMemo(() => withCorrectVoidBehavior(withReact(createEditor())), []);

I stole the plugin code from: https://github.com/ianstormtaylor/slate/issues/3991

Solution 2:[2]

Editing the source that SlateJS gave, I just added a paragraph node within the insertImage() function.

SlateJS Source:

const insertImage = (editor, url) => {
  const text = { text: '' }
  const image = { type: 'image', url, children: [text] }
  Transforms.insertNodes(editor, image)
}

Edit To:

const insertImage = (editor, url) => {
  const text = { text: '' }
  const image = [
    { 
      type: 'image', 
      url, 
      children: [text] 
    }, 
    {
      type: 'paragraph',
      children: [text],
    }
  ];

  Transforms.insertNodes(editor, image);
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tarkan
Solution 2 Asolace