mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-21 09:22:09 +03:00
Required a lot of changes to provide at least a decent attempt at proper editor teardown control. Also updates HtmlDescriptionFilter and testing to address issue with bad child iteration which could lead to missed items. Renamed editor version from comments to basic as it'll also be used for item descriptions.
143 lines
5.5 KiB
TypeScript
143 lines
5.5 KiB
TypeScript
import {createEditor, LexicalEditor} from 'lexical';
|
|
import {createEmptyHistoryState, registerHistory} from '@lexical/history';
|
|
import {registerRichText} from '@lexical/rich-text';
|
|
import {mergeRegister} from '@lexical/utils';
|
|
import {getNodesForBasicEditor, getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
|
|
import {buildEditorUI} from "./ui";
|
|
import {focusEditor, getEditorContentAsHtml, setEditorContentFromHtml} from "./utils/actions";
|
|
import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
|
|
import {EditorUiContext} from "./ui/framework/core";
|
|
import {listen as listenToCommonEvents} from "./services/common-events";
|
|
import {registerDropPasteHandling} from "./services/drop-paste-handling";
|
|
import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler";
|
|
import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler";
|
|
import {registerShortcuts} from "./services/shortcuts";
|
|
import {registerNodeResizer} from "./ui/framework/helpers/node-resizer";
|
|
import {registerKeyboardHandling} from "./services/keyboard-handling";
|
|
import {registerAutoLinks} from "./services/auto-links";
|
|
import {contextToolbars, getBasicEditorToolbar, getMainEditorFullToolbar} from "./ui/defaults/toolbars";
|
|
import {modals} from "./ui/defaults/modals";
|
|
import {CodeBlockDecorator} from "./ui/decorators/code-block";
|
|
import {DiagramDecorator} from "./ui/decorators/diagram";
|
|
|
|
const theme = {
|
|
text: {
|
|
bold: 'editor-theme-bold',
|
|
code: 'editor-theme-code',
|
|
italic: 'editor-theme-italic',
|
|
strikethrough: 'editor-theme-strikethrough',
|
|
subscript: 'editor-theme-subscript',
|
|
superscript: 'editor-theme-superscript',
|
|
underline: 'editor-theme-underline',
|
|
underlineStrikethrough: 'editor-theme-underline-strikethrough',
|
|
}
|
|
};
|
|
|
|
export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
|
|
const editor = createEditor({
|
|
namespace: 'BookStackPageEditor',
|
|
nodes: getNodesForPageEditor(),
|
|
onError: console.error,
|
|
theme: theme,
|
|
});
|
|
const context: EditorUiContext = buildEditorUI(container, editor, {
|
|
...options,
|
|
editorClass: 'page-content',
|
|
});
|
|
editor.setRootElement(context.editorDOM);
|
|
|
|
mergeRegister(
|
|
registerRichText(editor),
|
|
registerHistory(editor, createEmptyHistoryState(), 300),
|
|
registerShortcuts(context),
|
|
registerKeyboardHandling(context),
|
|
registerTableResizer(editor, context.scrollDOM),
|
|
registerTableSelectionHandler(editor),
|
|
registerTaskListHandler(editor, context.editorDOM),
|
|
registerDropPasteHandling(context),
|
|
registerNodeResizer(context),
|
|
registerAutoLinks(editor),
|
|
);
|
|
|
|
// Register toolbars, modals & decorators
|
|
context.manager.setToolbar(getMainEditorFullToolbar(context));
|
|
for (const key of Object.keys(contextToolbars)) {
|
|
context.manager.registerContextToolbar(key, contextToolbars[key]);
|
|
}
|
|
for (const key of Object.keys(modals)) {
|
|
context.manager.registerModal(key, modals[key]);
|
|
}
|
|
context.manager.registerDecoratorType('code', CodeBlockDecorator);
|
|
context.manager.registerDecoratorType('diagram', DiagramDecorator);
|
|
|
|
listenToCommonEvents(editor);
|
|
setEditorContentFromHtml(editor, htmlContent);
|
|
|
|
const debugView = document.getElementById('lexical-debug');
|
|
if (debugView) {
|
|
debugView.hidden = true;
|
|
editor.registerUpdateListener(({dirtyElements, dirtyLeaves, editorState, prevEditorState}) => {
|
|
// Debug logic
|
|
// console.log('editorState', editorState.toJSON());
|
|
debugView.textContent = JSON.stringify(editorState.toJSON(), null, 2);
|
|
});
|
|
}
|
|
|
|
// @ts-ignore
|
|
window.debugEditorState = () => {
|
|
return editor.getEditorState().toJSON();
|
|
};
|
|
|
|
registerCommonNodeMutationListeners(context);
|
|
|
|
return new SimpleWysiwygEditorInterface(context);
|
|
}
|
|
|
|
export function createBasicEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
|
|
const editor = createEditor({
|
|
namespace: 'BookStackBasicEditor',
|
|
nodes: getNodesForBasicEditor(),
|
|
onError: console.error,
|
|
theme: theme,
|
|
});
|
|
const context: EditorUiContext = buildEditorUI(container, editor, options);
|
|
editor.setRootElement(context.editorDOM);
|
|
|
|
const editorTeardown = mergeRegister(
|
|
registerRichText(editor),
|
|
registerHistory(editor, createEmptyHistoryState(), 300),
|
|
registerShortcuts(context),
|
|
registerAutoLinks(editor),
|
|
);
|
|
|
|
// Register toolbars, modals & decorators
|
|
context.manager.setToolbar(getBasicEditorToolbar(context));
|
|
context.manager.registerContextToolbar('link', contextToolbars.link);
|
|
context.manager.registerModal('link', modals.link);
|
|
context.manager.onTeardown(editorTeardown);
|
|
|
|
setEditorContentFromHtml(editor, htmlContent);
|
|
|
|
return new SimpleWysiwygEditorInterface(context);
|
|
}
|
|
|
|
export class SimpleWysiwygEditorInterface {
|
|
protected context: EditorUiContext;
|
|
|
|
constructor(context: EditorUiContext) {
|
|
this.context = context;
|
|
}
|
|
|
|
async getContentAsHtml(): Promise<string> {
|
|
return await getEditorContentAsHtml(this.context.editor);
|
|
}
|
|
|
|
focus(): void {
|
|
focusEditor(this.context.editor);
|
|
}
|
|
|
|
remove() {
|
|
this.context.editorDOM.remove();
|
|
this.context.manager.teardown();
|
|
}
|
|
} |