javascriptrichtextslate.js

How to add paragraph after image within a Slate text editor?


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

Same behavior in this example.


Solution

  • 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