]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/drop-handling.ts
Lexical: Completed out table menu elements, logic pending
[bookstack] / resources / js / wysiwyg / drop-handling.ts
1 import {
2     $isDecoratorNode,
3     LexicalEditor,
4     LexicalNode
5 } from "lexical";
6 import {
7     $getNearestBlockNodeForCoords,
8     $htmlToBlockNodes,
9     $insertNewBlockNodesAtSelection,
10     $selectSingleNode
11 } from "./helpers";
12
13 function $getNodeFromMouseEvent(event: MouseEvent, editor: LexicalEditor): LexicalNode|null {
14     const x = event.clientX;
15     const y = event.clientY;
16     const dom = document.elementFromPoint(x, y);
17     if (!dom) {
18         return null;
19     }
20
21     return $getNearestBlockNodeForCoords(editor, event.clientX, event.clientY);
22 }
23
24 function $insertNodesAtEvent(nodes: LexicalNode[], event: DragEvent, editor: LexicalEditor) {
25     const positionNode = $getNodeFromMouseEvent(event, editor);
26
27     if (positionNode) {
28         $selectSingleNode(positionNode);
29     }
30
31     $insertNewBlockNodesAtSelection(nodes, true);
32
33     if (!$isDecoratorNode(positionNode) || !positionNode?.getTextContent()) {
34         positionNode?.remove();
35     }
36 }
37
38 async function insertTemplateToEditor(editor: LexicalEditor, templateId: string, event: DragEvent) {
39     const resp = await window.$http.get(`/templates/${templateId}`);
40     const data = (resp.data || {html: ''}) as {html: string}
41     const html: string = data.html || '';
42
43     editor.update(() => {
44         const newNodes = $htmlToBlockNodes(editor, html);
45         $insertNodesAtEvent(newNodes, event, editor);
46     });
47 }
48
49 function createDropListener(editor: LexicalEditor): (event: DragEvent) => void {
50     return (event: DragEvent) => {
51         // Template handling
52         const templateId = event.dataTransfer?.getData('bookstack/template') || '';
53         if (templateId) {
54             insertTemplateToEditor(editor, templateId, event);
55             event.preventDefault();
56             return;
57         }
58
59         // HTML contents drop
60         const html = event.dataTransfer?.getData('text/html') || '';
61         if (html) {
62             editor.update(() => {
63                 const newNodes = $htmlToBlockNodes(editor, html);
64                 $insertNodesAtEvent(newNodes, event, editor);
65             });
66             event.preventDefault();
67             return;
68         }
69     };
70 }
71
72 export function handleDropEvents(editor: LexicalEditor) {
73     const dropListener = createDropListener(editor);
74
75     editor.registerRootListener((rootElement, prevRootElement) => {
76         rootElement?.addEventListener('drop', dropListener);
77         prevRootElement?.removeEventListener('drop', dropListener);
78     });
79 }