Lexical: Reorganised some logic into manager

This commit is contained in:
Dan Brown 2024-06-30 10:31:39 +01:00
parent f10ec3271a
commit 517c578a5f
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
4 changed files with 59 additions and 42 deletions

View File

@ -49,10 +49,10 @@ export class EditorDropdownButton extends EditorContainerUiElement {
handleDropdown(button, menu, () => {
this.open = true;
this.getContext().manager.triggerStateUpdate(this.button);
this.getContext().manager.triggerStateUpdateForElement(this.button);
}, () => {
this.open = false;
this.getContext().manager.triggerStateUpdate(this.button);
this.getContext().manager.triggerStateUpdateForElement(this.button);
});
return wrapper;

View File

@ -9,6 +9,7 @@ export type EditorUiStateUpdate = {
export type EditorUiContext = {
editor: LexicalEditor,
editorDOM: HTMLElement,
translate: (text: string) => string,
manager: EditorUIManager,
lastSelection: BaseSelection|null,

View File

@ -1,6 +1,9 @@
import {EditorFormModal, EditorFormModalDefinition} from "./modals";
import {EditorUiContext, EditorUiElement} from "./core";
import {EditorDecorator} from "./decorator";
import {EditorContainerUiElement, EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
import {EditorDecorator, EditorDecoratorAdapter} from "./decorator";
import {$getSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
import {DecoratorListener} from "lexical/LexicalEditor";
import type {NodeKey} from "lexical/LexicalNode";
export class EditorUIManager {
@ -9,9 +12,11 @@ export class EditorUIManager {
protected decoratorConstructorsByType: Record<string, typeof EditorDecorator> = {};
protected decoratorInstancesByNodeKey: Record<string, EditorDecorator> = {};
protected context: EditorUiContext|null = null;
protected toolbar: EditorContainerUiElement|null = null;
setContext(context: EditorUiContext) {
this.context = context;
this.setupEditor(context.editor);
}
getContext(): EditorUiContext {
@ -22,7 +27,7 @@ export class EditorUIManager {
return this.context;
}
triggerStateUpdate(element: EditorUiElement) {
triggerStateUpdateForElement(element: EditorUiElement) {
element.updateState({
selection: null,
editor: this.getContext().editor
@ -49,7 +54,7 @@ export class EditorUIManager {
this.decoratorConstructorsByType[type] = decorator;
}
getDecorator(decoratorType: string, nodeKey: string): EditorDecorator {
protected getDecorator(decoratorType: string, nodeKey: string): EditorDecorator {
if (this.decoratorInstancesByNodeKey[nodeKey]) {
return this.decoratorInstancesByNodeKey[nodeKey];
}
@ -64,4 +69,47 @@ export class EditorUIManager {
this.decoratorInstancesByNodeKey[nodeKey] = decorator;
return decorator;
}
setToolbar(toolbar: EditorContainerUiElement) {
if (this.toolbar) {
this.toolbar.getDOMElement().remove();
}
this.toolbar = toolbar;
toolbar.setContext(this.getContext());
this.getContext().editorDOM.before(toolbar.getDOMElement());
}
protected triggerStateUpdate(state: EditorUiStateUpdate): void {
const context = this.getContext();
context.lastSelection = state.selection;
this.toolbar?.updateState(state);
}
protected setupEditor(editor: LexicalEditor) {
// Update button states on editor selection change
editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
this.triggerStateUpdate({
editor: editor,
selection: $getSelection(),
});
return false;
}, COMMAND_PRIORITY_LOW);
// Register our DOM decorate listener with the editor
const domDecorateListener: DecoratorListener<EditorDecoratorAdapter> = (decorators: Record<NodeKey, EditorDecoratorAdapter>) => {
const keys = Object.keys(decorators);
for (const key of keys) {
const decoratedEl = editor.getElementByKey(key);
const adapter = decorators[key];
const decorator = this.getDecorator(adapter.type, key);
decorator.setNode(adapter.getNode());
const decoratorEl = decorator.render(this.getContext());
if (decoratedEl) {
decoratedEl.append(decoratorEl);
}
}
}
editor.registerDecoratorListener(domDecorateListener);
}
}

View File

@ -1,15 +1,7 @@
import {
$getSelection,
COMMAND_PRIORITY_LOW,
LexicalEditor,
SELECTION_CHANGE_COMMAND
} from "lexical";
import {LexicalEditor} from "lexical";
import {getMainEditorFullToolbar} from "./toolbars";
import {EditorUIManager} from "./framework/manager";
import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
import {DecoratorListener} from "lexical/LexicalEditor";
import type {NodeKey} from "lexical/LexicalNode";
import {EditorDecoratorAdapter} from "./framework/decorator";
import {ImageDecorator} from "./decorators/image";
import {EditorUiContext} from "./framework/core";
@ -17,6 +9,7 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
const manager = new EditorUIManager();
const context: EditorUiContext = {
editor,
editorDOM: element,
manager,
translate: (text: string): string => text,
lastSelection: null,
@ -24,9 +17,7 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
manager.setContext(context);
// Create primary toolbar
const toolbar = getMainEditorFullToolbar();
toolbar.setContext(context);
element.before(toolbar.getDOMElement());
manager.setToolbar(getMainEditorFullToolbar());
// Register modals
manager.registerModal('link', {
@ -42,29 +33,6 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
form: sourceFormDefinition,
});
// Register decorator listener
// Maybe move to manager?
// Register image decorator listener
manager.registerDecoratorType('image', ImageDecorator);
const domDecorateListener: DecoratorListener<EditorDecoratorAdapter> = (decorators: Record<NodeKey, EditorDecoratorAdapter>) => {
const keys = Object.keys(decorators);
for (const key of keys) {
const decoratedEl = editor.getElementByKey(key);
const adapter = decorators[key];
const decorator = manager.getDecorator(adapter.type, key);
decorator.setNode(adapter.getNode());
const decoratorEl = decorator.render(context);
if (decoratedEl) {
decoratedEl.append(decoratorEl);
}
}
}
editor.registerDecoratorListener(domDecorateListener);
// Update button states on editor selection change
editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
const selection = $getSelection();
toolbar.updateState({editor, selection});
context.lastSelection = selection;
return false;
}, COMMAND_PRIORITY_LOW);
}