]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/blocks/link-field.ts
5a64cdc30fcbcf2ce2a1d33b85ab2b059f314624
[bookstack] / resources / js / wysiwyg / ui / framework / blocks / link-field.ts
1 import {EditorContainerUiElement} from "../core";
2 import {el} from "../../../utils/dom";
3 import {EditorFormField} from "../forms";
4 import {CustomHeadingNode} from "../../../nodes/custom-heading";
5 import {$getAllNodesOfType} from "../../../utils/nodes";
6 import {$isHeadingNode} from "@lexical/rich-text";
7 import {uniqueIdSmall} from "../../../../services/util";
8
9 export class LinkField extends EditorContainerUiElement {
10     protected input: EditorFormField;
11     protected headerMap = new Map<string, CustomHeadingNode>();
12
13     constructor(input: EditorFormField) {
14         super([input]);
15
16         this.input = input;
17     }
18
19     buildDOM(): HTMLElement {
20         const listId = 'editor-form-datalist-' + this.input.getName() + '-' + Date.now();
21         const inputOuterDOM = this.input.getDOMElement();
22         const inputFieldDOM = inputOuterDOM.querySelector('input');
23         inputFieldDOM?.setAttribute('list', listId);
24         inputFieldDOM?.setAttribute('autocomplete', 'off');
25         const datalist = el('datalist', {id: listId});
26
27         const container = el('div', {
28             class: 'editor-link-field-container',
29         }, [inputOuterDOM, datalist]);
30
31         inputFieldDOM?.addEventListener('focusin', () => {
32             this.updateDataList(datalist);
33         });
34
35         inputFieldDOM?.addEventListener('input', () => {
36             const value = inputFieldDOM.value;
37             const header = this.headerMap.get(value);
38             if (header) {
39                 this.updateFormFromHeader(header);
40             }
41         });
42
43         return container;
44     }
45
46     updateFormFromHeader(header: CustomHeadingNode) {
47         this.getHeaderIdAndText(header).then(({id, text}) => {
48             console.log('updating form', id, text);
49             const modal =  this.getContext().manager.getActiveModal('link');
50             if (modal) {
51                 modal.getForm().setValues({
52                     url: `#${id}`,
53                     text: text,
54                     title: text,
55                 });
56             }
57         });
58     }
59
60     getHeaderIdAndText(header: CustomHeadingNode): Promise<{id: string, text: string}> {
61         return new Promise((res) => {
62             this.getContext().editor.update(() => {
63                 let id = header.getId();
64                 console.log('header', id, header.__id);
65                 if (!id) {
66                     id = 'header-' + uniqueIdSmall();
67                     header.setId(id);
68                 }
69
70                 const text = header.getTextContent();
71                 res({id, text});
72             });
73         });
74     }
75
76     updateDataList(listEl: HTMLElement) {
77         this.getContext().editor.getEditorState().read(() => {
78             const headers = $getAllNodesOfType($isHeadingNode) as CustomHeadingNode[];
79
80             this.headerMap.clear();
81             const listEls: HTMLElement[] = [];
82
83             for (const header of headers) {
84                 const key = 'header-' + header.getKey();
85                 this.headerMap.set(key, header);
86                 listEls.push(el('option', {
87                     value: key,
88                     label: header.getTextContent().substring(0, 54),
89                 }));
90             }
91
92             listEl.innerHTML = '';
93             listEl.append(...listEls);
94         });
95     }
96 }