]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/services/keyboard-handling.ts
Lexical: Added single node enter handling
[bookstack] / resources / js / wysiwyg / services / keyboard-handling.ts
1 import {EditorUiContext} from "../ui/framework/core";
2 import {
3     $isDecoratorNode,
4     COMMAND_PRIORITY_LOW,
5     KEY_BACKSPACE_COMMAND,
6     KEY_DELETE_COMMAND,
7     KEY_ENTER_COMMAND,
8     LexicalEditor,
9     LexicalNode
10 } from "lexical";
11 import {$isImageNode} from "../nodes/image";
12 import {$isMediaNode} from "../nodes/media";
13 import {getLastSelection} from "../utils/selection";
14 import {$getNearestNodeBlockParent} from "../utils/nodes";
15 import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
16
17 function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
18     if (nodes.length === 1) {
19         const node = nodes[0];
20         if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node)) {
21             return true;
22         }
23     }
24
25     return false;
26 }
27
28 function deleteSingleSelectedNode(editor: LexicalEditor) {
29     const selectionNodes = getLastSelection(editor)?.getNodes() || [];
30     if (isSingleSelectedNode(selectionNodes)) {
31         editor.update(() => {
32             selectionNodes[0].remove();
33         });
34     }
35 }
36
37 function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
38     const selectionNodes = getLastSelection(editor)?.getNodes() || [];
39     if (isSingleSelectedNode(selectionNodes)) {
40         const node = selectionNodes[0];
41         const nearestBlock = $getNearestNodeBlockParent(node) || node;
42         if (nearestBlock) {
43             requestAnimationFrame(() => {
44                 editor.update(() => {
45                     const newParagraph = $createCustomParagraphNode();
46                     nearestBlock.insertAfter(newParagraph);
47                     newParagraph.select();
48                 });
49             });
50             event?.preventDefault();
51             return true;
52         }
53     }
54
55     return false;
56 }
57
58 export function registerKeyboardHandling(context: EditorUiContext): () => void {
59     const unregisterBackspace = context.editor.registerCommand(KEY_BACKSPACE_COMMAND, (): boolean => {
60         deleteSingleSelectedNode(context.editor);
61         return false;
62     }, COMMAND_PRIORITY_LOW);
63
64     const unregisterDelete = context.editor.registerCommand(KEY_DELETE_COMMAND, (): boolean => {
65         deleteSingleSelectedNode(context.editor);
66         return false;
67     }, COMMAND_PRIORITY_LOW);
68
69     const unregisterEnter = context.editor.registerCommand(KEY_ENTER_COMMAND, (event): boolean => {
70         return insertAfterSingleSelectedNode(context.editor, event);
71     }, COMMAND_PRIORITY_LOW);
72
73     return () => {
74         unregisterBackspace();
75         unregisterDelete();
76         unregisterEnter();
77     };
78 }