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:
8
dev/docs/wysiwyg-js-api.md
Normal file
8
dev/docs/wysiwyg-js-api.md
Normal 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
|
||||||
11
resources/js/wysiwyg/api/api.ts
Normal file
11
resources/js/wysiwyg/api/api.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
resources/js/wysiwyg/api/ui.ts
Normal file
79
resources/js/wysiwyg/api/ui.ts
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user