1

I am trying to use tiptap mention extension but with a list of suggestions from infinitequery but I just couldn't do it.

Is it even possible? Please share any ideas you might have

I tried next to everything. Matter of fact, the code I will paste is the code of a desperate man and I just put here for you to have some references so if you don't understand it, it's okay because me too (just too many things attempted).

Anyway after many attempts I realized that the issue is that no matter what I do, in the items functions of suggestions (in const editor) I can't update the filter of the infinitequery so it always ends up empty/undefined.

"use client";

import tippy, { Instance, Props } from "tippy.js";
import { useEditor, EditorContent, ReactRenderer } from "@tiptap/react";
import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph";
import Text from "@tiptap/extension-text";
import Mention from "@tiptap/extension-mention";
import { MentionList } from "./MentionList";
import { Editor } from "@tiptap/core"; // To type the `editor` instance
import { useInfiniteQuery } from "@tanstack/react-query";
import kyInstance from "@/lib/ky";
import { UserData, UsersPage } from "@/lib/type";
import { useCallback, useEffect, useState } from "react";

interface MentionListProps {
  items: UserData[];
  command: (item: { user: UserData }) => void;
}

export default function MyEditor() {
  const [mentionQuery, setMentionQuery] = useState("");

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetching,
    isFetchingNextPage,
    status,
  } = useInfiniteQuery({
    queryKey: ["users", mentionQuery],
    queryFn: ({ pageParam }) => {
      const searchParams: Record<string, string> = {};
      if (pageParam) {
        searchParams.cursor = pageParam;
      }
      if (!!mentionQuery) {
        searchParams.filter = mentionQuery;
      }

      return kyInstance.get("/api/users", { searchParams }).json<UsersPage>();
    },
    initialPageParam: null as string | null,
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  });

  const Myitems = ({ query }: { query: string }) => {
    const {
      data,
      fetchNextPage,
      hasNextPage,
      isFetching,
      isFetchingNextPage,
      status,
    } = useInfiniteQuery({
      queryKey: ["users", query],
      queryFn: ({ pageParam }) => {
        const searchParams: Record<string, string> = {};
        if (pageParam) {
          searchParams.cursor = pageParam;
        }
        if (!!mentionQuery) {
          searchParams.filter = query;
        }

        return kyInstance.get("/api/users", { searchParams }).json<UsersPage>();
      },
      initialPageParam: null as string | null,
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    });

    const allUsers = data ? data.pages.flatMap((page) => page.users) : [];
    console.log("users", allUsers);
    return allUsers;
  };

  // const getMentionSuggestions = useCallback(async ({ query }: { query: string }) => {
  //   const allUsers = data ? data.pages.flatMap((page) => page.users) : [];
  //   setMentionQuery(query);
  //   // Trigger a refetch when the query changes
  //   await fetchNextPage();
  //   console.log("users", allUsers)
  //   return allUsers;
  // }, [data,fetchNextPage]);

  const editor: Editor | null = useEditor({
    extensions: [
      Document,
      Paragraph,
      Text,
      Mention.configure({
        HTMLAttributes: {
          class: "mention",
        },
        suggestion: {
          items: async ({ query }: { query: string }) => {
            setMentionQuery(query);
            const users = data?.pages.flatMap((page) => page.users);
            setTimeout(() => {
              const users = data?.pages.flatMap((page) => page.users);
              console.log("users", users);
              return users || [];
            }, 5000);
            return users || [];
          },
          render: () => {
            let reactRenderer: ReactRenderer<MentionListProps>;
            let popup: Instance<Props>;

            return {
              onStart: (props) => {
                reactRenderer = new ReactRenderer(MentionList, {
                  props,
                  editor: props.editor,
                });

                popup = tippy(document.body, {
                  getReferenceClientRect: () => {
                    if (props.clientRect) {
                      return props.clientRect() as DOMRect;
                    }
                    // Fallback to an empty DOMRect if `props.clientRect` is null
                    return new DOMRect();
                  },
                  appendTo: () => document.body,
                  content: reactRenderer.element,
                  showOnCreate: true,
                  interactive: true,
                  trigger: "manual",
                  placement: "bottom-start",
                });
              },
              onUpdate(props) {
                reactRenderer.updateProps(props);

                popup.setProps({
                  getReferenceClientRect: () => {
                    if (props.clientRect) {
                      return props.clientRect() as DOMRect;
                    }
                    // Return a default client rect or handle as needed
                    return new DOMRect(); // Provides a non-null fallback
                  },
                });
              },
              // onKeyDown(props) {
              //   return reactRenderer.ref?.onKeyDown(props);
              // },
              onExit() {
                popup.destroy();
                reactRenderer.destroy();
              },
            };
          },
        },
      }),
    ],
    content: `
      <p>
        What do you all think about the new <span data-mention="Winona Ryder"></span> movie?
      </p>
    `,
  });

  return (
    <div>
      <EditorContent editor={editor} />
    </div>
  );
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.