Home > OS >  How do I remove default behavior of "shift-enter" or add custom behavior in tiptap react
How do I remove default behavior of "shift-enter" or add custom behavior in tiptap react

Time:11-10

Implementing ordered and unordered list using tiptap react. I want functionality like when user currently in any list or any list is active in text-area of tiptap then by hit the enter key message should send with a single ordered list. The problem is when I need to add another line in same list, using shift enter key for jumping in new line with updated number of list for example:

"1. currently writing  in text-area" `hitting shift-enter key` 
the expected output is 
"1. currently writing  in text-area"
"2. some other line" 

but I got

"1. currently writing  in text-area"
"2. "
" " `cursor is in next line` .

I found out the problem is with every shift enter hit tiptap adds
tag and add a simple break line in between any working node.

I tried with adding

const removeBlankSpace = () => {
      const abc = editor?.getHTML().replace("<br>", " ") || "";
      editor?.commands.setContent(abc);
    };

removing blank space or
tag explicitly and setting up inside editor.commands.setContent

const editor = useEditor({
      extensions: [
        Document.extend({
          // @ts-ignore
          addCommands() {
            return {
              setNewParagraph:
                () =>
                // @ts-ignore
                ({ commands, editor }) => {
                  const { state } = editor;
                  const { selection } = state;
                  const { $head } = selection;
                  const position = $head.after();

                  return commands.insertContentAt(
                    { from: position, to: position },
                    { type: Paragraph.name } // Note this is adding a paragraph
                  );
                },
            };
          },
          addKeyboardShortcuts() {
            return {
              Enter: () => {
                emitCustomEvent("enter-key-tiptap");
                return true;
              },
            };
          },
        }),

        StarterKit.configure({
          document: false,
          heading: {
            levels: [1, 2, 3, 4],
          },
        }),
        Strike,
        Highlight.configure({ multicolor: true }),
        Link.configure({
          openOnClick: true,
        }),
        Emoji.configure({
          emojis: [...gitHubEmojis],
          enableEmoticons: true,
          forceFallbackImages: true,
        }),
        Placeholder.configure({
          showOnlyWhenEditable: false,
          showOnlyCurrent: true,
          includeChildren: true,
          placeholder: () => "Send a message",
        }),
        Link.configure({
          openOnClick: false,
        }),
        MentionExtension,
        MentionTipTap.configure(),
      ],
      editorProps: {
        attributes: {
          class: `prose prose-sm custom-note sm:prose-sm lg:prose-lg xl:prose-2xl mx-auto focus:outline-none`,
        },
        handleKeyDown(view, event) {
          // For the purpose of detect key inside ordered and unordered list.
          if (keyPressCheck.key === "Shift" && event.key === "Enter") {
            console.log("emit new list");
            emitCustomEvent("new-line-list");
          }
          keyPressCheck.key = event.key;
          if (keyPressCheck.key !== "Shift" && event.key === "Enter") {
            emitCustomEvent("enter-for-list");
          }
        },
      },
      autofocus: "end",
      onUpdate({ editor, transaction }) {
        onChange(editor as Editor);
      },
      content: ``,
    });
    const handleEnterKeyForList = () => {
      if (editor?.can().splitListItem("listItem")) {
        console.log("enter key submit here");
        onMessageSend();
      }
    };
    // editor?.commands.setContent("<ul><li><p>jg</p></li><li><p></p></li></ul>");
    const removeBlankSpace = () => {
      console.log(
        "changes donw ------------------------------",
        editor?.getHTML()
      );
      // const abc = editor?.getHTML().replace("<br>", " ") || "";
      // console.log("abc: ", abc);
      // editor?.commands.setContent(abc);
    };

    const handleNewLineUpdate = () => {
      if (
        editor?.isActive("orderedList") ||
        (editor?.isActive("bulletList") &&
          editor.can().splitListItem("listItem"))
      ) {
        editor?.chain().focus().splitListItem("listItem").run();
        removeBlankSpace();
      }
    };
    
    useCustomEventListener("enter-key-tiptap", handleEnterKey);
    useCustomEventListener("enter-for-list", handleEnterKeyForList);
    useCustomEventListener("new-line-list", handleNewLineUpdate);

CodePudding user response:

I found out solution just now with the reference from here.

created a new extension and implement it inside editor=({extensions:[ShifEnter]}).

Instead of creating new

tag. I removed the hardBreak functionality pf tiptap.

here is the component shiftEnter: =>

import { Node } from "@tiptap/core";
export const ShiftEnter = Node.create({
  name: "shiftEnter",
  addCommands() {
    return {
      setNewParagraph:
        () =>
        ({ commands, editor }) => {
          const { state } = editor;
          const { selection } = state;
          const { $head } = selection;
          const position = $head.after();
          return commands.insertContentAt({ from: position, to: position });
        },
    };
  },
  addKeyboardShortcuts() {
    return {
      "Shift-Enter": () => this.editor.commands.setNewParagraph(),
    };
  },
});`

CodePudding user response:

If you still want to keep HardBreak I would suggest to extend the HardBreak extension, that will check if you should allow hard break.

import HardBreakTiptap from "@tiptap/extension-hard-break";
import { Node as ProseMirrorNode } from "prosemirror-model";
export interface HardBreakOtions {
  excludeTypes?: string[];
}
declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    extendedHardBreak: {
      setHardBreak: () => ReturnType;
      setBasedOnType: () => ReturnType;
    };
  }
}
const HardBreak = HardBreakTiptap.extend<HardBreakOtions>({
  addOptions() {
    return {
      ...this.parent?.(),
      excludeTypes: [],
    };
  },
  addCommands() {
    return {
      ...this.parent?.(),
      setBasedOnType:
        () =>
        ({ commands, chain, state, editor }) => {
          const { selection } = state;
          const excludeTypes = this.options.excludeTypes;
          if (
            excludeTypes &&
            selection.$anchor.path.some((item: ProseMirrorNode | number) => {
              if (item instanceof ProseMirrorNode) {
              }
              return (
                item instanceof ProseMirrorNode &&
                excludeTypes.includes(item?.type?.name)
              );
            })
          ) {
            //
            return false;
          }

          return commands.setHardBreak();
        },
    };
  },
  addKeyboardShortcuts() {
    return {
      ...this.parent?.(),
      "Shift-Enter": () => {
        return this.editor.commands.setBasedOnType();
      },
    };
  },
});

export default HardBreak;

If you use StarterKit make sure to turn of hardBreak that otherwise it will cause issues

 StarterKit.configure({
        hardBreak: false,
      }),

You can then configure the custom hard break with the the types you want to block

 HardBreak.configure({
        excludeTypes: ["listItem"],
      })

Note that the extended HardBreak will not do anything if the type is excluded and you will have to add the keyboard shortcut to the extensions you want. In your case

import ListItemTipTap from "@tiptap/extension-list-item";

const ListItem = ListItemTipTap.extend({
  addKeyboardShortcuts() {
    return {
      ...this.parent?.(),
      "Shift-Enter": () => {
        return this.editor.commands.splitListItem(this.name);
      },
    };
  },
});

export default ListItem;

Note that you will have to exclude even this from StartKit:

StarterKit.configure({
        hardBreak: false,
        listItem: false,
      }),
  • Related