ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.63k stars 2.3k forks source link

Backspacing the last character in a paragraph in a nodeview inserts indestructible BR or empties the entire node #1311

Closed thatsjonsense closed 3 years ago

thatsjonsense commented 3 years ago

Description I'm using a NodeView with block content via . When I backspace in a paragraph with only one character, one of two things will happen: (1) The entire parent node gets erased and replaced with a BR tag (2) Just the paragraph node gets replaced by a BR tag, which can't be deleted or navigated past via the cursor

Steps to reproduce the bug Please see https://gamma-editor-prototype-git-jon-nodeviewbug-gamma-app.vercel.app/nodeviewbug2. In the second line, or on any line you add, try backspacing the last character on a line.

Oddly enough, the exact same issue doesn't repro when I put the exact same code in a codesandbox: https://codesandbox.io/s/node-view-bug-simplified-b3m2k?file=/src/App.tsx

The rendered HTML is the same. I can't figure out what the difference could be. Some hypotheses:

It sounds similar to https://github.com/ProseMirror/prosemirror-view/blob/c6ed7c2af3ff9f1eb9b1b2ef1e288eb92269b92a/src/domchange.js#L23 and https://github.com/ProseMirror/prosemirror/issues/930, but those appear to already be fixed and the issue doesn't seem to be browser specific. I've cross-posted this issue to ProseMirror, but I'm not sure if the problem is in Tiptap or Prosemirror. The issue only happens when using a NodeView. When I remove the nodeviews, it works perfectly.

CodeSandbox I created a CodeSandbox to help you debug the issue: https://codesandbox.io/s/node-view-bug-simplified-b3m2k?file=/src/App.tsx But see above - I actually can't get it to repro here!

Screenshot, video, or GIF https://www.loom.com/share/3e8f5d83a006409195bf005306edc3ae

Environment?

Thanks in advance for your help!

philippkuehn commented 3 years ago

Hey, you are using some old versions of all tiptap packages.

Please update all packages with: yarn upgrade-interactive --latest and try again.

thatsjonsense commented 3 years ago

This worked! Thank you @philippkuehn

raulpopadineti commented 3 years ago

I get the same issue with the @tiptap/react package. The placeholder doesn't get displayed due to the <p><br/</p> once the editor is emptied.

This is my React component:

import * as React from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";

type Props = {
  placeholder: string;
  initialValue: null | undefined | string;
  className: string;
  onChange: (newValue: string) => void;
};

export const TiptapEditor = ({
  placeholder,
  initialValue,
  className,
  onChange,
}: Props & Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">) => {
  const editor = useEditor({
    extensions: [StarterKit],
    editorProps: {
      attributes: {
        class: className,
        placeholder: placeholder,
      },
    },
    parseOptions: {
      preserveWhitespace: "full",
    },
    autofocus: true,
    injectCSS: false,
    content: initialValue,
    onUpdate({ editor }) {
      onChange(editor.getHTML());
    },
  });

  return <EditorContent editor={editor} />;
};

I've installed tiptap a few minutes ago so I'm on the latest version. I've also tried to set the preserveWhitespace to false, but it did not help. Any ideas on how to fix it?

philippkuehn commented 3 years ago

@raulpopadineti can you create a sandbox reproducing the error please?

raulpopadineti commented 3 years ago

@philippkuehn here it is: https://codesandbox.io/s/awesome-water-jk9pq

philippkuehn commented 3 years ago

@raulpopadineti this is expected. the
is needed by ProseMirror to render an empty paragraph. otherwise a paragraph would have a height of 0px. but this
is not in your editor state if you want to render a placeholder you should use the Placeholder extension: https://www.tiptap.dev/api/extensions/placeholder

raulpopadineti commented 3 years ago

Ah, makes sense. Thanks for the help! 😁

raulpopadineti commented 3 years ago

@philippkuehn is it possible to register a custom node, besides the paragraph node, to toggle the is-empty CSS class and add the placeholder? (e.g.: for a figcaption).

I tried registering the extension like shown below, but it doesn't seem to work.

Placeholder.configure({
        placeholder: ({ node }) => {
          if (
            node.type.name === "figcaption" &&
            node.isLeaf &&
            !node.textContent
          ) {
            return "Type caption for image (optional)";
          } else {
            return placeholder;
          }
        },
      }),
raulpopadineti commented 3 years ago

Ended up extending the Placeholder extension with some custom logic inside the decorations props to match my Figure node. All good 🙌🏻