2021-04-30 12:20:31 +02:00
|
|
|
import { SEARCH_PERSONS } from "@/graphql/search";
|
|
|
|
import { VueRenderer } from "@tiptap/vue-2";
|
|
|
|
import tippy from "tippy.js";
|
|
|
|
import MentionList from "./MentionList.vue";
|
2021-05-12 18:10:07 +02:00
|
|
|
import { ApolloClient } from "@apollo/client/core/ApolloClient";
|
2021-04-30 12:20:31 +02:00
|
|
|
import apolloProvider from "@/vue-apollo";
|
2021-05-02 19:27:23 +02:00
|
|
|
import { IPerson } from "@/types/actor";
|
|
|
|
import pDebounce from "p-debounce";
|
2021-05-12 18:10:07 +02:00
|
|
|
import { NormalizedCacheObject } from "@apollo/client/cache/inmemory/types";
|
2022-01-18 12:51:37 +01:00
|
|
|
import { MentionOptions } from "@tiptap/extension-mention";
|
|
|
|
import { Editor } from "@tiptap/core";
|
2021-04-30 12:20:31 +02:00
|
|
|
|
2021-05-17 19:01:08 +02:00
|
|
|
const client =
|
|
|
|
apolloProvider.defaultClient as ApolloClient<NormalizedCacheObject>;
|
2021-04-30 12:20:31 +02:00
|
|
|
|
2021-05-02 19:27:23 +02:00
|
|
|
const fetchItems = async (query: string): Promise<IPerson[]> => {
|
|
|
|
const result = await client.query({
|
|
|
|
query: SEARCH_PERSONS,
|
|
|
|
variables: {
|
|
|
|
searchText: query,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
// TipTap doesn't handle async for onFilter, hence the following line.
|
|
|
|
return result.data.searchPersons.elements;
|
|
|
|
};
|
|
|
|
|
|
|
|
const debouncedFetchItems = pDebounce(fetchItems, 200);
|
|
|
|
|
2022-01-18 12:51:37 +01:00
|
|
|
const mentionOptions: MentionOptions = {
|
2021-04-30 12:20:31 +02:00
|
|
|
HTMLAttributes: {
|
|
|
|
class: "mention",
|
2021-11-07 14:59:20 +01:00
|
|
|
dir: "ltr",
|
2021-04-30 12:20:31 +02:00
|
|
|
},
|
2022-01-18 12:51:37 +01:00
|
|
|
renderLabel({ options, node }) {
|
|
|
|
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`;
|
|
|
|
},
|
2021-04-30 12:20:31 +02:00
|
|
|
suggestion: {
|
2022-01-18 12:51:37 +01:00
|
|
|
items: async ({
|
|
|
|
query,
|
|
|
|
}: {
|
|
|
|
query: string;
|
|
|
|
editor: Editor;
|
|
|
|
}): Promise<IPerson[]> => {
|
2021-05-02 19:27:23 +02:00
|
|
|
if (query === "") {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
return await debouncedFetchItems(query);
|
2021-04-30 12:20:31 +02:00
|
|
|
},
|
|
|
|
render: () => {
|
|
|
|
let component: VueRenderer;
|
|
|
|
let popup: any;
|
|
|
|
|
|
|
|
return {
|
|
|
|
onStart: (props: any) => {
|
|
|
|
component = new VueRenderer(MentionList, {
|
|
|
|
parent: this,
|
|
|
|
propsData: props,
|
|
|
|
});
|
|
|
|
|
|
|
|
popup = tippy("body", {
|
|
|
|
getReferenceClientRect: props.clientRect,
|
|
|
|
appendTo: () => document.body,
|
|
|
|
content: component.element,
|
|
|
|
showOnCreate: true,
|
|
|
|
interactive: true,
|
|
|
|
trigger: "manual",
|
|
|
|
placement: "bottom-start",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onUpdate(props: any) {
|
|
|
|
component.updateProps(props);
|
|
|
|
|
|
|
|
popup[0].setProps({
|
|
|
|
getReferenceClientRect: props.clientRect,
|
|
|
|
});
|
|
|
|
},
|
2021-05-02 19:27:23 +02:00
|
|
|
onKeyDown(props: any) {
|
2021-05-02 20:41:23 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore
|
|
|
|
return component.ref?.onKeyDown(props);
|
2021-05-02 19:27:23 +02:00
|
|
|
},
|
2021-04-30 12:20:31 +02:00
|
|
|
onExit() {
|
2022-01-18 12:51:37 +01:00
|
|
|
if (popup && popup[0]) {
|
|
|
|
popup[0].destroy();
|
|
|
|
}
|
|
|
|
if (component) {
|
|
|
|
component.destroy();
|
|
|
|
}
|
2021-04-30 12:20:31 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export default mentionOptions;
|