mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Added base context toolbar logic
This commit is contained in:
parent
517c578a5f
commit
c9a03c5b01
@ -4,7 +4,7 @@ import {EditorDecorator, EditorDecoratorAdapter} from "./decorator";
|
|||||||
import {$getSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
|
import {$getSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
|
||||||
import {DecoratorListener} from "lexical/LexicalEditor";
|
import {DecoratorListener} from "lexical/LexicalEditor";
|
||||||
import type {NodeKey} from "lexical/LexicalNode";
|
import type {NodeKey} from "lexical/LexicalNode";
|
||||||
|
import {EditorContextToolbar, EditorContextToolbarDefinition} from "./toolbars";
|
||||||
|
|
||||||
export class EditorUIManager {
|
export class EditorUIManager {
|
||||||
|
|
||||||
@ -13,6 +13,8 @@ export class EditorUIManager {
|
|||||||
protected decoratorInstancesByNodeKey: Record<string, EditorDecorator> = {};
|
protected decoratorInstancesByNodeKey: Record<string, EditorDecorator> = {};
|
||||||
protected context: EditorUiContext|null = null;
|
protected context: EditorUiContext|null = null;
|
||||||
protected toolbar: EditorContainerUiElement|null = null;
|
protected toolbar: EditorContainerUiElement|null = null;
|
||||||
|
protected contextToolbarDefinitionsByKey: Record<string, EditorContextToolbarDefinition> = {};
|
||||||
|
protected activeContextToolbars: EditorContextToolbar[] = [];
|
||||||
|
|
||||||
setContext(context: EditorUiContext) {
|
setContext(context: EditorUiContext) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -80,10 +82,59 @@ export class EditorUIManager {
|
|||||||
this.getContext().editorDOM.before(toolbar.getDOMElement());
|
this.getContext().editorDOM.before(toolbar.getDOMElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected triggerStateUpdate(state: EditorUiStateUpdate): void {
|
registerContextToolbar(key: string, definition: EditorContextToolbarDefinition) {
|
||||||
|
this.contextToolbarDefinitionsByKey[key] = definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected triggerStateUpdate(update: EditorUiStateUpdate): void {
|
||||||
const context = this.getContext();
|
const context = this.getContext();
|
||||||
context.lastSelection = state.selection;
|
context.lastSelection = update.selection;
|
||||||
this.toolbar?.updateState(state);
|
this.toolbar?.updateState(update);
|
||||||
|
this.updateContextToolbars(update);
|
||||||
|
for (const toolbar of this.activeContextToolbars) {
|
||||||
|
toolbar.updateState(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updateContextToolbars(update: EditorUiStateUpdate): void {
|
||||||
|
for (const toolbar of this.activeContextToolbars) {
|
||||||
|
toolbar.empty();
|
||||||
|
toolbar.getDOMElement().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = (update.selection?.getNodes() || [])[0] || null;
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = update.editor.getElementByKey(node.getKey());
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolbarKeys = Object.keys(this.contextToolbarDefinitionsByKey);
|
||||||
|
const contentByTarget = new Map<HTMLElement, EditorUiElement[]>();
|
||||||
|
for (const key of toolbarKeys) {
|
||||||
|
const definition = this.contextToolbarDefinitionsByKey[key];
|
||||||
|
const matchingElem = ((element.closest(definition.selector)) || (element.querySelector(definition.selector))) as HTMLElement|null;
|
||||||
|
if (matchingElem) {
|
||||||
|
const targetEl = definition.displayTargetLocator ? definition.displayTargetLocator(matchingElem) : matchingElem;
|
||||||
|
if (!contentByTarget.has(targetEl)) {
|
||||||
|
contentByTarget.set(targetEl, [])
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
contentByTarget.get(targetEl).push(...definition.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [target, contents] of contentByTarget) {
|
||||||
|
const toolbar = new EditorContextToolbar(contents);
|
||||||
|
toolbar.setContext(this.getContext());
|
||||||
|
this.activeContextToolbars.push(toolbar);
|
||||||
|
|
||||||
|
this.getContext().editorDOM.after(toolbar.getDOMElement());
|
||||||
|
toolbar.attachTo(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected setupEditor(editor: LexicalEditor) {
|
protected setupEditor(editor: LexicalEditor) {
|
||||||
|
36
resources/js/wysiwyg/ui/framework/toolbars.ts
Normal file
36
resources/js/wysiwyg/ui/framework/toolbars.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {EditorContainerUiElement, EditorUiElement} from "./core";
|
||||||
|
import {el} from "../../helpers";
|
||||||
|
|
||||||
|
export type EditorContextToolbarDefinition = {
|
||||||
|
selector: string;
|
||||||
|
content: EditorUiElement[],
|
||||||
|
displayTargetLocator?: (originalTarget: HTMLElement) => HTMLElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class EditorContextToolbar extends EditorContainerUiElement {
|
||||||
|
|
||||||
|
protected buildDOM(): HTMLElement {
|
||||||
|
return el('div', {
|
||||||
|
class: 'editor-context-toolbar',
|
||||||
|
}, this.getChildren().map(child => child.getDOMElement()));
|
||||||
|
}
|
||||||
|
|
||||||
|
attachTo(target: HTMLElement) {
|
||||||
|
// Todo - attach to target position
|
||||||
|
console.log('attaching context toolbar to', target);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(children: EditorUiElement[]) {
|
||||||
|
this.addChildren(...children);
|
||||||
|
const dom = this.getDOMElement();
|
||||||
|
dom.append(...children.map(child => child.getDOMElement()));
|
||||||
|
}
|
||||||
|
|
||||||
|
empty() {
|
||||||
|
const children = this.getChildren();
|
||||||
|
for (const child of children) {
|
||||||
|
child.getDOMElement().remove();
|
||||||
|
}
|
||||||
|
this.removeChildren(...children);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import {LexicalEditor} from "lexical";
|
import {LexicalEditor} from "lexical";
|
||||||
import {getMainEditorFullToolbar} from "./toolbars";
|
import {getImageToolbarContent, getMainEditorFullToolbar} from "./toolbars";
|
||||||
import {EditorUIManager} from "./framework/manager";
|
import {EditorUIManager} from "./framework/manager";
|
||||||
import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
|
import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
|
||||||
import {ImageDecorator} from "./decorators/image";
|
import {ImageDecorator} from "./decorators/image";
|
||||||
@ -33,6 +33,15 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) {
|
|||||||
form: sourceFormDefinition,
|
form: sourceFormDefinition,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register context toolbars
|
||||||
|
manager.registerContextToolbar('image', {
|
||||||
|
selector: 'img',
|
||||||
|
content: getImageToolbarContent(),
|
||||||
|
displayTargetLocator(originalTarget: HTMLElement) {
|
||||||
|
return originalTarget.closest('a') || originalTarget;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Register image decorator listener
|
// Register image decorator listener
|
||||||
manager.registerDecoratorType('image', ImageDecorator);
|
manager.registerDecoratorType('image', ImageDecorator);
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ import {
|
|||||||
undo,
|
undo,
|
||||||
warningCallout
|
warningCallout
|
||||||
} from "./defaults/button-definitions";
|
} from "./defaults/button-definitions";
|
||||||
import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext} from "./framework/core";
|
import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext, EditorUiElement} from "./framework/core";
|
||||||
import {el} from "../helpers";
|
import {el} from "../helpers";
|
||||||
import {EditorFormatMenu} from "./framework/blocks/format-menu";
|
import {EditorFormatMenu} from "./framework/blocks/format-menu";
|
||||||
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
|
||||||
@ -88,3 +88,7 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getImageToolbarContent(): EditorUiElement[] {
|
||||||
|
return [new EditorButton(image)];
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user