Skip to content

Commit

Permalink
Refactor/make spanner plugin (#97)
Browse files Browse the repository at this point in the history
* feat: make spanner plugin

* feat: add config to spanner plugin

* fix: linting errors

* feat: config mountpoint

* fix: unit test
  • Loading branch information
vincentdchan authored Dec 4, 2023
1 parent ec86a36 commit 805c9b8
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 106 deletions.
6 changes: 1 addition & 5 deletions packages/blocky-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ export * from "./block/basic";
export { ContentBlock } from "./block/contentBlock";
export { CustomBlock } from "./block/customBlock";
export { makeDefaultIdGenerator, type IdGenerator } from "./helper/idHelper";
export {
type SpannerFactory,
type SpannerInstance,
SpannerDelegate,
} from "./view/spannerDelegate";
export { type ToolbarFactory, type Toolbar } from "./view/toolbarDelegate";
export { FollowerWidget } from "./view/followerWidget";
export {
Expand All @@ -25,6 +20,7 @@ export {
PluginRegistry,
PluginContext,
} from "./registry/pluginRegistry";
export * from "./plugins/spannerPlugin";
export { BlockRegistry } from "./registry/blockRegistry";
export { type SpanStyle, SpanRegistry } from "./registry/spanRegistry";
export {
Expand Down
13 changes: 13 additions & 0 deletions packages/blocky-core/src/plugins/spannerPlugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SpannerPlugin } from "./spannerPlugin";
import {
SpannerDelegate,
type SpannerFactory,
type SpannerInstance,
} from "./spannerDelegate";

export {
SpannerPlugin,
SpannerDelegate,
type SpannerFactory,
type SpannerInstance,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { SpannerDelegate, SpannerInstance } from "./spannerDelegate";
import { EditorController } from "./controller";
import { BlockDataElement } from "..";
import { EditorController } from "../../view/controller";
import { BlockDataElement, Editor } from "../..";

describe("SpannerDelegate", () => {
it("focusedNode", () => {
Expand All @@ -14,7 +14,8 @@ describe("SpannerDelegate", () => {

const mount = document.createElement("div");

const delegate = new SpannerDelegate(editorController, () => {
const editor = Editor.fromController(mount, editorController);
const delegate = new SpannerDelegate(editor, () => {
return spannerInstance;
});
delegate.mount(mount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { type IDisposable } from "blocky-common/es";
import type { EditorController } from "@pkg/view/controller";
import type { BlockDataElement } from "@pkg/data";
import { UIDelegate } from "./uiDelegate";
import { UIDelegate } from "@pkg/view/uiDelegate";
import { fromEvent, takeUntil } from "rxjs";
import type { Editor } from "@pkg/view/editor";

export interface SpannerInstance extends IDisposable {
onFocusedNodeChanged?(focusedNode: BlockDataElement | undefined): void;
Expand Down Expand Up @@ -30,14 +31,7 @@ export class SpannerDelegate extends UIDelegate {
this.#instance?.onFocusedNodeChanged?.(v);
}

get width(): number {
return 28;
}

constructor(
private editorController: EditorController,
private factory: SpannerFactory
) {
constructor(public editor: Editor, private factory: SpannerFactory) {
super("blocky-editor-spanner-delegate blocky-cm-noselect");
// draggable
this.container.setAttribute("draggable", "true");
Expand All @@ -49,17 +43,14 @@ export class SpannerDelegate extends UIDelegate {
}

#handleDragStart() {
const editor = this.editorController.editor;
if (!editor) {
return;
}
editor.darggingNode = this.focusedNode;
this.editor.darggingNode = this.focusedNode;
}

override mount(parent: HTMLElement): void {
super.mount(parent);

this.#instance = this.factory(this.container, this.editorController, this);
const editorController = this.editor.controller;
this.#instance = this.factory(this.container, editorController, this);
if (this.#instance) {
this.disposables.push(this.#instance);
}
Expand Down
90 changes: 90 additions & 0 deletions packages/blocky-core/src/plugins/spannerPlugin/spannerPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { BlockDataElement, Editor, IPlugin, PluginContext } from "../..";
import { take, takeUntil, fromEvent } from "rxjs";
import { SpannerDelegate, SpannerFactory } from "./spannerDelegate";
import { type Position } from "blocky-common/es";

const defaultWidth = 48;

export interface SpannerPluginOptions {
factory: SpannerFactory;
width?: number;
mountPoint?: HTMLElement;
}

export class SpannerPlugin implements IPlugin {
deletage: SpannerDelegate | undefined;
name = "spanner";

constructor(readonly options: SpannerPluginOptions) {}

get container(): HTMLElement | undefined {
return this.options.mountPoint ?? this.deletage?.editor.container;
}

onInitialized(context: PluginContext): void {
const { editor, dispose$ } = context;
this.deletage = new SpannerDelegate(editor, this.options.factory);

const container = this.options.mountPoint ?? editor.container;
this.deletage.mount(container);

editor.placeSpannerAt$
.pipe(takeUntil(dispose$))
.subscribe(({ blockContainer, node }) => {
this.placeSpannerAt(editor, blockContainer, node);
});

fromEvent(container, "mouseleave")
.pipe(takeUntil(dispose$))
.subscribe(() => {
this.deletage?.hide();
});

dispose$.pipe(take(1)).subscribe(() => {
this.deletage?.dispose();
this.deletage = undefined;
});
}

protected placeSpannerAt(
editor: Editor,
blockContainer: HTMLElement,
node: BlockDataElement
) {
if (!this.deletage) {
return;
}
const block = editor.state.blocks.get(node.id);
if (!block) {
return;
}
let { x, y } = this.getRelativeOffsetByDom(blockContainer);
const offset = block.getSpannerOffset();
x += offset.x;
y += offset.y;
x -= this.width;
this.deletage.focusedNode = node;
this.deletage.show();
this.deletage.setPosition(x, y);
}

get width(): number {
return this.options.width ?? defaultWidth;
}

/**
* Get the element's relative position to the container of the editor.
*/
protected getRelativeOffsetByDom(element: HTMLElement): Position {
const container = this.container;
if (!container) {
return { x: 0, y: 0 };
}
const containerRect = container.getBoundingClientRect();
const blockRect = element.getBoundingClientRect();
return {
x: blockRect.x - containerRect.x,
y: blockRect.y - containerRect.y,
};
}
}
4 changes: 0 additions & 4 deletions packages/blocky-core/src/registry/pluginRegistry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ describe("PluginRegistry", () => {
const plugin: IPlugin = {
name: "test",
onInitialized() {},
onDispose() {},
};
const onInitSpy = vi.spyOn(plugin, "onInitialized");
const disposeSpy = vi.spyOn(plugin, "onDispose");
const pluginRegistry = new PluginRegistry([plugin]);
const editorController = new EditorController("user");
const dom = document.createElement("div");
const editor = Editor.fromController(dom, editorController);
pluginRegistry.initAllPlugins(editor);

expect(onInitSpy).toBeCalledTimes(1);
expect(disposeSpy).toBeCalledTimes(0);

pluginRegistry.unload("test");
expect(disposeSpy).toBeCalledTimes(1);
});
});
3 changes: 0 additions & 3 deletions packages/blocky-core/src/registry/pluginRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ export interface IPlugin {
*/
onInitialized?(context: PluginContext): void;

onDispose?(context: PluginContext): void;

onPaste?(evt: BlockyPasteEvent): void;
}

Expand Down Expand Up @@ -99,7 +97,6 @@ export class PluginRegistry {
if (context) {
context.dispose();
this.contexts.delete(name);
plugin.onDispose?.(context);
}
}

Expand Down
2 changes: 0 additions & 2 deletions packages/blocky-core/src/view/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { PluginRegistry, type IPlugin } from "@pkg/registry/pluginRegistry";
import { SpanRegistry } from "@pkg/registry/spanRegistry";
import { EmbedRegistry } from "@pkg/registry/embedRegistry";
import { HTMLConverter } from "@pkg/helper/htmlConverter";
import { type SpannerFactory } from "@pkg/view/spannerDelegate";
import { type ToolbarFactory } from "@pkg/view/toolbarDelegate";
import { type IdGenerator, makeDefaultIdGenerator } from "@pkg/helper/idHelper";
import { BlockPasteEvent, TryParsePastedDOMEvent } from "@pkg/block/basic";
Expand Down Expand Up @@ -89,7 +88,6 @@ export interface IEditorControllerOptions {
blockRegistry?: BlockRegistry;
embedRegistry?: EmbedRegistry;
idGenerator?: IdGenerator;
spannerFactory?: SpannerFactory;
toolbarFactory?: ToolbarFactory;

/**
Expand Down
Loading

1 comment on commit 805c9b8

@vercel
Copy link

@vercel vercel bot commented on 805c9b8 Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.