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";
22 import {registerMouseHandling} from "./services/mouse-handling";
26 bold: 'editor-theme-bold',
27 code: 'editor-theme-code',
28 italic: 'editor-theme-italic',
29 strikethrough: 'editor-theme-strikethrough',
30 subscript: 'editor-theme-subscript',
31 superscript: 'editor-theme-superscript',
32 underline: 'editor-theme-underline',
33 underlineStrikethrough: 'editor-theme-underline-strikethrough',
37 export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
38 const editor = createEditor({
39 namespace: 'BookStackPageEditor',
40 nodes: getNodesForPageEditor(),
41 onError: console.error,
44 const context: EditorUiContext = buildEditorUI(container, editor, {
46 editorClass: 'page-content',
48 editor.setRootElement(context.editorDOM);
51 registerRichText(editor),
52 registerHistory(editor, createEmptyHistoryState(), 300),
53 registerShortcuts(context),
54 registerKeyboardHandling(context),
55 registerMouseHandling(context),
56 registerTableResizer(editor, context.scrollDOM),
57 registerTableSelectionHandler(editor),
58 registerTaskListHandler(editor, context.editorDOM),
59 registerDropPasteHandling(context),
60 registerNodeResizer(context),
61 registerAutoLinks(editor),
64 // Register toolbars, modals & decorators
65 context.manager.setToolbar(getMainEditorFullToolbar(context));
66 for (const key of Object.keys(contextToolbars)) {
67 context.manager.registerContextToolbar(key, contextToolbars[key]);
69 for (const key of Object.keys(modals)) {
70 context.manager.registerModal(key, modals[key]);
72 context.manager.registerDecoratorType('code', CodeBlockDecorator);
73 context.manager.registerDecoratorType('diagram', DiagramDecorator);
75 listenToCommonEvents(editor);
76 setEditorContentFromHtml(editor, htmlContent);
78 const debugView = document.getElementById('lexical-debug');
80 debugView.hidden = true;
81 editor.registerUpdateListener(({dirtyElements, dirtyLeaves, editorState, prevEditorState}) => {
83 // console.log('editorState', editorState.toJSON());
84 debugView.textContent = JSON.stringify(editorState.toJSON(), null, 2);
89 window.debugEditorState = () => {
90 return editor.getEditorState().toJSON();
93 registerCommonNodeMutationListeners(context);
95 return new SimpleWysiwygEditorInterface(context);
98 export function createBasicEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
99 const editor = createEditor({
100 namespace: 'BookStackBasicEditor',
101 nodes: getNodesForBasicEditor(),
102 onError: console.error,
105 const context: EditorUiContext = buildEditorUI(container, editor, options);
106 editor.setRootElement(context.editorDOM);
108 const editorTeardown = mergeRegister(
109 registerRichText(editor),
110 registerHistory(editor, createEmptyHistoryState(), 300),
111 registerShortcuts(context),
112 registerAutoLinks(editor),
115 // Register toolbars, modals & decorators
116 context.manager.setToolbar(getBasicEditorToolbar(context));
117 context.manager.registerContextToolbar('link', contextToolbars.link);
118 context.manager.registerModal('link', modals.link);
119 context.manager.onTeardown(editorTeardown);
121 setEditorContentFromHtml(editor, htmlContent);
123 return new SimpleWysiwygEditorInterface(context);
126 export class SimpleWysiwygEditorInterface {
127 protected context: EditorUiContext;
128 protected onChangeListeners: (() => void)[] = [];
129 protected editorListenerTeardown: (() => void)|null = null;
131 constructor(context: EditorUiContext) {
132 this.context = context;
135 async getContentAsHtml(): Promise<string> {
136 return await getEditorContentAsHtml(this.context.editor);
139 onChange(listener: () => void) {
140 this.onChangeListeners.push(listener);
141 this.startListeningToChanges();
145 focusEditor(this.context.editor);
149 this.context.manager.teardown();
150 this.context.containerDOM.remove();
151 if (this.editorListenerTeardown) {
152 this.editorListenerTeardown();
156 protected startListeningToChanges(): void {
157 if (this.editorListenerTeardown) {
161 this.editorListenerTeardown = this.context.editor.registerUpdateListener(() => {
162 for (const listener of this.onChangeListeners) {