1 import {createEditor, LexicalEditor} from 'lexical';
2 import {createEmptyHistoryState, registerHistory} from '@lexical/history';
3 import {registerRichText} from '@lexical/rich-text';
4 import {mergeRegister} from '@lexical/utils';
5 import {getNodesForBasicEditor, getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
6 import {buildEditorUI} from "./ui";
7 import {focusEditor, getEditorContentAsHtml, setEditorContentFromHtml} from "./utils/actions";
8 import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
9 import {EditorUiContext} from "./ui/framework/core";
10 import {listen as listenToCommonEvents} from "./services/common-events";
11 import {registerDropPasteHandling} from "./services/drop-paste-handling";
12 import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler";
13 import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler";
14 import {registerShortcuts} from "./services/shortcuts";
15 import {registerNodeResizer} from "./ui/framework/helpers/node-resizer";
16 import {registerKeyboardHandling} from "./services/keyboard-handling";
17 import {registerAutoLinks} from "./services/auto-links";
18 import {contextToolbars, getBasicEditorToolbar, getMainEditorFullToolbar} from "./ui/defaults/toolbars";
19 import {modals} from "./ui/defaults/modals";
20 import {CodeBlockDecorator} from "./ui/decorators/code-block";
21 import {DiagramDecorator} from "./ui/decorators/diagram";
25 bold: 'editor-theme-bold',
26 code: 'editor-theme-code',
27 italic: 'editor-theme-italic',
28 strikethrough: 'editor-theme-strikethrough',
29 subscript: 'editor-theme-subscript',
30 superscript: 'editor-theme-superscript',
31 underline: 'editor-theme-underline',
32 underlineStrikethrough: 'editor-theme-underline-strikethrough',
36 export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
37 const editor = createEditor({
38 namespace: 'BookStackPageEditor',
39 nodes: getNodesForPageEditor(),
40 onError: console.error,
43 const context: EditorUiContext = buildEditorUI(container, editor, {
45 editorClass: 'page-content',
47 editor.setRootElement(context.editorDOM);
50 registerRichText(editor),
51 registerHistory(editor, createEmptyHistoryState(), 300),
52 registerShortcuts(context),
53 registerKeyboardHandling(context),
54 registerTableResizer(editor, context.scrollDOM),
55 registerTableSelectionHandler(editor),
56 registerTaskListHandler(editor, context.editorDOM),
57 registerDropPasteHandling(context),
58 registerNodeResizer(context),
59 registerAutoLinks(editor),
62 // Register toolbars, modals & decorators
63 context.manager.setToolbar(getMainEditorFullToolbar(context));
64 for (const key of Object.keys(contextToolbars)) {
65 context.manager.registerContextToolbar(key, contextToolbars[key]);
67 for (const key of Object.keys(modals)) {
68 context.manager.registerModal(key, modals[key]);
70 context.manager.registerDecoratorType('code', CodeBlockDecorator);
71 context.manager.registerDecoratorType('diagram', DiagramDecorator);
73 listenToCommonEvents(editor);
74 setEditorContentFromHtml(editor, htmlContent);
76 const debugView = document.getElementById('lexical-debug');
78 debugView.hidden = true;
79 editor.registerUpdateListener(({dirtyElements, dirtyLeaves, editorState, prevEditorState}) => {
81 // console.log('editorState', editorState.toJSON());
82 debugView.textContent = JSON.stringify(editorState.toJSON(), null, 2);
87 window.debugEditorState = () => {
88 return editor.getEditorState().toJSON();
91 registerCommonNodeMutationListeners(context);
93 return new SimpleWysiwygEditorInterface(context);
96 export function createBasicEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
97 const editor = createEditor({
98 namespace: 'BookStackBasicEditor',
99 nodes: getNodesForBasicEditor(),
100 onError: console.error,
103 const context: EditorUiContext = buildEditorUI(container, editor, options);
104 editor.setRootElement(context.editorDOM);
106 const editorTeardown = mergeRegister(
107 registerRichText(editor),
108 registerHistory(editor, createEmptyHistoryState(), 300),
109 registerShortcuts(context),
110 registerAutoLinks(editor),
113 // Register toolbars, modals & decorators
114 context.manager.setToolbar(getBasicEditorToolbar(context));
115 context.manager.registerContextToolbar('link', contextToolbars.link);
116 context.manager.registerModal('link', modals.link);
117 context.manager.onTeardown(editorTeardown);
119 setEditorContentFromHtml(editor, htmlContent);
121 return new SimpleWysiwygEditorInterface(context);
124 export class SimpleWysiwygEditorInterface {
125 protected context: EditorUiContext;
126 protected onChangeListeners: (() => void)[] = [];
127 protected editorListenerTeardown: (() => void)|null = null;
129 constructor(context: EditorUiContext) {
130 this.context = context;
133 async getContentAsHtml(): Promise<string> {
134 return await getEditorContentAsHtml(this.context.editor);
137 onChange(listener: () => void) {
138 this.onChangeListeners.push(listener);
139 this.startListeningToChanges();
143 focusEditor(this.context.editor);
147 this.context.manager.teardown();
148 this.context.containerDOM.remove();
149 if (this.editorListenerTeardown) {
150 this.editorListenerTeardown();
154 protected startListeningToChanges(): void {
155 if (this.editorListenerTeardown) {
159 this.editorListenerTeardown = this.context.editor.registerUpdateListener(() => {
160 for (const listener of this.onChangeListeners) {