Skip to content
File

Blob: src/client/components/editor/controllers/emoji/suggestion-overlay.ts

typescript55 lines
1import type { Editor } from "@tiptap/core";
2import type { EmojiItem } from "@tiptap/extension-emoji";
3import { mountEditorRenderer } from "../menu/renderer";
4import {
5 EmojiSuggestionPanel,
6 type EmojiSuggestionPanelHandle,
7 type EmojiSuggestionPanelProps,
8} from "./suggestion-panel";
9 
10interface EmojiSuggestionOverlayOpts {
11 contextElement?: HTMLElement | null;
12 items: EmojiItem[];
13 command: (item: EmojiItem) => void;
14 clientRect: (() => DOMRect | null) | null;
15 onClose?: () => void;
16}
17 
18export interface EmojiSuggestionOverlayHandle {
19 updateProps(next: Partial<EmojiSuggestionOverlayOpts>): void;
20 onKeyDown(event: KeyboardEvent): boolean;
21 destroy(): void;
22}
23 
24export function mountEmojiSuggestion(editor: Editor, opts: EmojiSuggestionOverlayOpts): EmojiSuggestionOverlayHandle {
25 const component = mountEditorRenderer<EmojiSuggestionPanelHandle, EmojiSuggestionPanelProps>(
26 editor,
27 EmojiSuggestionPanel,
28 {
29 items: opts.items,
30 command: (item: EmojiItem) => opts.command(item),
31 clientRect: opts.clientRect,
32 contextElement: opts.contextElement ?? editor.view.dom,
33 onClose: opts.onClose,
34 },
35 );
36 
37 return {
38 updateProps(next) {
39 component.updateProps({
40 ...(next.items !== undefined && { items: next.items }),
41 ...(next.command !== undefined && { command: (item: EmojiItem) => next.command!(item) }),
42 ...(next.clientRect !== undefined && { clientRect: next.clientRect }),
43 ...(next.contextElement !== undefined && { contextElement: next.contextElement }),
44 ...(next.onClose !== undefined && { onClose: next.onClose }),
45 });
46 },
47 onKeyDown(event) {
48 return component.ref?.onKeyDown(event) ?? false;
49 },
50 destroy() {
51 component.destroy();
52 },
53 };
54}