1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-12-16 10:02:22 +03:00

Lexical: Started dev API outline

This commit is contained in:
Dan Brown
2025-11-30 17:02:17 +00:00
parent 9e8088f186
commit 9d732d8dd8
7 changed files with 129 additions and 10 deletions

View File

@@ -0,0 +1,8 @@
# WYSIWYG JavaScript API
TODO - Link to this from JS code doc.
TODO - Create JS events and add to the js public events doc.
TODO - Document the JS API.
TODO - Add testing coverage

View File

@@ -0,0 +1,11 @@
import {EditorApiUiModule} from "./ui";
import {EditorUiContext} from "../ui/framework/core";
export class EditorApi {
public ui: EditorApiUiModule;
constructor(context: EditorUiContext) {
this.ui = new EditorApiUiModule(context);
}
}

View File

@@ -0,0 +1,79 @@
import {EditorButton} from "../ui/framework/buttons";
import {EditorUiContext} from "../ui/framework/core";
import {EditorOverflowContainer} from "../ui/framework/blocks/overflow-container";
type EditorApiButtonOptions = {
label?: string;
icon?: string;
onClick: () => void;
};
class EditorApiButton {
#button: EditorButton;
#isActive: boolean = false;
constructor(options: EditorApiButtonOptions, context: EditorUiContext) {
this.#button = new EditorButton({
label: options.label || '',
icon: options.icon || '',
action: () => {
options.onClick();
},
isActive: () => this.#isActive,
});
this.#button.setContext(context);
}
setActive(active: boolean = true): void {
this.#isActive = active;
this.#button.setActiveState(active);
}
_getOriginalModel() {
return this.#button;
}
}
class EditorApiToolbarSection {
#section: EditorOverflowContainer;
label: string;
constructor(section: EditorOverflowContainer) {
this.#section = section;
this.label = section.getLabel();
}
getLabel(): string {
return this.#section.getLabel();
}
addButton(button: EditorApiButton, targetIndex: number = -1): void {
this.#section.addChild(button._getOriginalModel(), targetIndex);
this.#section.rebuildDOM();
}
}
export class EditorApiUiModule {
#context: EditorUiContext;
constructor(context: EditorUiContext) {
this.#context = context;
}
createButton(options: EditorApiButtonOptions): EditorApiButton {
return new EditorApiButton(options, this.#context);
}
getToolbarSections(): EditorApiToolbarSection[] {
const toolbar = this.#context.manager.getToolbar();
if (!toolbar) {
return [];
}
const sections = toolbar.getChildren();
return sections.filter(section => {
return section instanceof EditorOverflowContainer;
}).map(section => new EditorApiToolbarSection(section));
}
}

View File

@@ -21,6 +21,7 @@ import {CodeBlockDecorator} from "./ui/decorators/code-block";
import {DiagramDecorator} from "./ui/decorators/diagram"; import {DiagramDecorator} from "./ui/decorators/diagram";
import {registerMouseHandling} from "./services/mouse-handling"; import {registerMouseHandling} from "./services/mouse-handling";
import {registerSelectionHandling} from "./services/selection-handling"; import {registerSelectionHandling} from "./services/selection-handling";
import {EditorApi} from "./api/api";
const theme = { const theme = {
text: { text: {
@@ -94,6 +95,12 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
registerCommonNodeMutationListeners(context); registerCommonNodeMutationListeners(context);
// TODO - Emit this as a public event instead
// TODO - Add support to basic editor below
const api = new EditorApi(context);
// @ts-ignore
window.editorApi = api;
return new SimpleWysiwygEditorInterface(context); return new SimpleWysiwygEditorInterface(context);
} }

View File

@@ -88,7 +88,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
return new EditorSimpleClassContainer('editor-toolbar-main', [ return new EditorSimpleClassContainer('editor-toolbar-main', [
// History state // History state
new EditorOverflowContainer(2, [ new EditorOverflowContainer('history', 2, [
new EditorButton(undo), new EditorButton(undo),
new EditorButton(redo), new EditorButton(redo),
]), ]),
@@ -110,7 +110,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
]), ]),
// Inline formats // Inline formats
new EditorOverflowContainer(6, [ new EditorOverflowContainer('inline_formats', 6, [
new EditorButton(bold), new EditorButton(bold),
new EditorButton(italic), new EditorButton(italic),
new EditorButton(underline), new EditorButton(underline),
@@ -128,7 +128,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
]), ]),
// Alignment // Alignment
new EditorOverflowContainer(6, [ new EditorOverflowContainer('alignment', 6, [
new EditorButton(alignLeft), new EditorButton(alignLeft),
new EditorButton(alignCenter), new EditorButton(alignCenter),
new EditorButton(alignRight), new EditorButton(alignRight),
@@ -138,7 +138,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
].filter(x => x !== null)), ].filter(x => x !== null)),
// Lists // Lists
new EditorOverflowContainer(3, [ new EditorOverflowContainer('lists', 3, [
new EditorButton(bulletList), new EditorButton(bulletList),
new EditorButton(numberList), new EditorButton(numberList),
new EditorButton(taskList), new EditorButton(taskList),
@@ -147,7 +147,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
]), ]),
// Insert types // Insert types
new EditorOverflowContainer(4, [ new EditorOverflowContainer('inserts', 4, [
new EditorButton(link), new EditorButton(link),
new EditorDropdownButton({button: table, direction: 'vertical', showAside: false}, [ new EditorDropdownButton({button: table, direction: 'vertical', showAside: false}, [
@@ -200,7 +200,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
]), ]),
// Meta elements // Meta elements
new EditorOverflowContainer(3, [ new EditorOverflowContainer('meta', 3, [
new EditorButton(source), new EditorButton(source),
new EditorButton(about), new EditorButton(about),
new EditorButton(fullscreen), new EditorButton(fullscreen),
@@ -261,16 +261,16 @@ export const contextToolbars: Record<string, EditorContextToolbarDefinition> = {
selector: 'td,th', selector: 'td,th',
content() { content() {
return [ return [
new EditorOverflowContainer(2, [ new EditorOverflowContainer('table', 2, [
new EditorButton(tableProperties), new EditorButton(tableProperties),
new EditorButton(deleteTable), new EditorButton(deleteTable),
]), ]),
new EditorOverflowContainer(3, [ new EditorOverflowContainer('table_row',3, [
new EditorButton(insertRowAbove), new EditorButton(insertRowAbove),
new EditorButton(insertRowBelow), new EditorButton(insertRowBelow),
new EditorButton(deleteRow), new EditorButton(deleteRow),
]), ]),
new EditorOverflowContainer(3, [ new EditorOverflowContainer('table_column', 3, [
new EditorButton(insertColumnBefore), new EditorButton(insertColumnBefore),
new EditorButton(insertColumnAfter), new EditorButton(insertColumnAfter),
new EditorButton(deleteColumn), new EditorButton(deleteColumn),

View File

@@ -9,9 +9,11 @@ export class EditorOverflowContainer extends EditorContainerUiElement {
protected size: number; protected size: number;
protected overflowButton: EditorDropdownButton; protected overflowButton: EditorDropdownButton;
protected content: EditorUiElement[]; protected content: EditorUiElement[];
protected label: string;
constructor(size: number, children: EditorUiElement[]) { constructor(label: string, size: number, children: EditorUiElement[]) {
super(children); super(children);
this.label = label;
this.size = size; this.size = size;
this.content = children; this.content = children;
this.overflowButton = new EditorDropdownButton({ this.overflowButton = new EditorDropdownButton({
@@ -24,6 +26,11 @@ export class EditorOverflowContainer extends EditorContainerUiElement {
this.addChildren(this.overflowButton); this.addChildren(this.overflowButton);
} }
addChild(child: EditorUiElement, targetIndex: number = -1): void {
this.content.splice(targetIndex, 0, child);
this.addChildren(child);
}
protected buildDOM(): HTMLElement { protected buildDOM(): HTMLElement {
const slicePosition = this.content.length > this.size ? this.size - 1 : this.size; const slicePosition = this.content.length > this.size ? this.size - 1 : this.size;
const visibleChildren = this.content.slice(0, slicePosition); const visibleChildren = this.content.slice(0, slicePosition);
@@ -41,5 +48,8 @@ export class EditorOverflowContainer extends EditorContainerUiElement {
}, visibleElements); }, visibleElements);
} }
getLabel(): string {
return this.label;
}
} }

View File

@@ -109,6 +109,10 @@ export class EditorUIManager {
this.getContext().containerDOM.prepend(toolbar.getDOMElement()); this.getContext().containerDOM.prepend(toolbar.getDOMElement());
} }
getToolbar(): EditorContainerUiElement|null {
return this.toolbar;
}
registerContextToolbar(key: string, definition: EditorContextToolbarDefinition) { registerContextToolbar(key: string, definition: EditorContextToolbarDefinition) {
this.contextToolbarDefinitionsByKey[key] = definition; this.contextToolbarDefinitionsByKey[key] = definition;
} }