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