Skip to content

Commit

Permalink
refactor: plugin registry (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentdchan authored Nov 12, 2023
1 parent 584e085 commit c8c6f0c
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 85 deletions.
18 changes: 18 additions & 0 deletions packages/blocky-core/css/blocky-core.css
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,21 @@
.blocky-search-range.active {
background-color: rgb(235, 154, 4);
}

.blocky-bold-text {
font-weight: 600;
}

.blocky-italic-text {
font-style: italic;
}

.blocky-code-text {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: var(--color-neutral-muted);
border-radius: 6px;
}
17 changes: 0 additions & 17 deletions packages/blocky-core/css/styled-text-plugin.css

This file was deleted.

6 changes: 5 additions & 1 deletion packages/blocky-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export {
type CollaborativeCursorFactory,
type CollaborativeCursorClient,
} from "./view/collaborativeCursors";
export { type IPlugin, PluginRegistry } from "./registry/pluginRegistry";
export {
type IPlugin,
PluginRegistry,
PluginContext,
} from "./registry/pluginRegistry";
export { BlockRegistry } from "./registry/blockRegistry";
export { type SpanStyle, SpanRegistry } from "./registry/spanRegistry";
export {
Expand Down
13 changes: 9 additions & 4 deletions packages/blocky-core/src/plugins/bulletListPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import {
TextType,
Delta,
} from "blocky-data";
import { filter } from "rxjs";
import { filter, takeUntil } from "rxjs";
import {
getTextTypeForTextBlock,
TextBlock,
type TextInputEvent,
type IPlugin,
type Editor,
type PluginContext,
} from "@pkg/index";

function makeBulletListPlugin(): IPlugin {
Expand Down Expand Up @@ -128,11 +129,15 @@ function makeBulletListPlugin(): IPlugin {
};
return {
name: "bullet-list",
onInitialized(editor: Editor) {
onInitialized(context: PluginContext) {
const { editor, dispose$ } = context;
editor.textInput
.pipe(filter((evt) => evt.blockElement.t === TextBlock.Name))
.pipe(
takeUntil(dispose$),
filter((evt) => evt.blockElement.t === TextBlock.Name)
)
.subscribe(handleTextInputEvent(editor));
editor.keyDown.subscribe(handleKeydown(editor));
editor.keyDown.pipe(takeUntil(dispose$)).subscribe(handleKeydown(editor));
},
};
}
Expand Down
8 changes: 5 additions & 3 deletions packages/blocky-core/src/plugins/codeTextPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type Editor, type IPlugin, TextBlock } from "@pkg/index";
import { type PluginContext, type IPlugin, TextBlock } from "@pkg/index";
import { CursorState, Changeset, Delta } from "blocky-data";
import { isHotkey } from "is-hotkey";
import { isUndefined } from "lodash-es";
import { takeUntil } from "rxjs";

class CodeTextDetector {
#cursor: CursorState;
Expand Down Expand Up @@ -45,8 +46,9 @@ function makeCodeTextPlugin(): IPlugin {
className: "blocky-code-text",
},
],
onInitialized(editor: Editor) {
editor.keyDown.subscribe((e: KeyboardEvent) => {
onInitialized(context: PluginContext) {
const { editor, dispose$ } = context;
editor.keyDown.pipe(takeUntil(dispose$)).subscribe((e: KeyboardEvent) => {
if (isHotkey("mod+m", e)) {
e.preventDefault();
editor.controller.formatTextOnSelectedText({
Expand Down
12 changes: 8 additions & 4 deletions packages/blocky-core/src/plugins/headingsPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { isWhiteSpace } from "blocky-common/es";
import { type Editor, type IPlugin, TextBlock } from "@pkg/index";
import { type IPlugin, TextBlock, type PluginContext } from "@pkg/index";
import { Changeset, TextType, Delta } from "blocky-data";
import { isNumber, isString } from "lodash-es";
import { filter } from "rxjs";
import { filter, takeUntil } from "rxjs";

function makeHeadingsPlugin(): IPlugin {
return {
name: "headings",
onInitialized(editor: Editor) {
onInitialized(context: PluginContext) {
const { editor, dispose$ } = context;
editor.textInput
.pipe(filter((evt) => evt.blockElement.t === TextBlock.Name)) // don't apply on Title block
.pipe(
takeUntil(dispose$),
filter((evt) => evt.blockElement.t === TextBlock.Name) // don't apply on Title block
)
.subscribe((evt) => {
const { beforeString, blockElement } = evt;
const { state } = editor;
Expand Down
8 changes: 5 additions & 3 deletions packages/blocky-core/src/plugins/styledTextPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Editor, IPlugin } from "@pkg/index";
import type { IPlugin, PluginContext } from "@pkg/index";
import { isHotkey } from "is-hotkey";
import { takeUntil } from "rxjs";

/**
* This plugin is used to make the editor support bolded text.
Expand Down Expand Up @@ -29,8 +30,9 @@ function makeStyledTextPlugin(): IPlugin {
},
},
],
onInitialized(editor: Editor) {
editor.keyDown.subscribe((e: KeyboardEvent) => {
onInitialized(context: PluginContext) {
const { editor, dispose$ } = context;
editor.keyDown.pipe(takeUntil(dispose$)).subscribe((e: KeyboardEvent) => {
if (isHotkey("mod+b", e)) {
e.preventDefault();
editor.controller.formatTextOnSelectedText({
Expand Down
50 changes: 33 additions & 17 deletions packages/blocky-core/src/registry/pluginRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { IBlockDefinition } from "@pkg/block/basic";
import type { Editor } from "@pkg/view/editor";
import type { SpanStyle } from "./spanRegistry";
import type { EmbedDefinition } from "./embedRegistry";
import { Subject } from "rxjs";

export type AfterFn = () => void;

Expand Down Expand Up @@ -36,12 +37,24 @@ export interface IPlugin {
/**
* Will be triggered when the editor is initialized.
*/
onInitialized?(editor: Editor): void;
onInitialized?(context: PluginContext): void;

onDispose?(context: PluginContext): void;
}

export class PluginContext {
dispose$ = new Subject<void>();
constructor(public editor: Editor) {}

dispose() {
this.dispose$.next();
}
}

export class PluginRegistry {
#hook: HookMethods = Object.create(null);
#markedPlugin: Set<string> = new Set();
plugins: Map<string, IPlugin> = new Map();
private contexts: Map<string, PluginContext> = new Map();

constructor(plugins?: IPlugin[]) {
for (const name of allowHookName) {
Expand All @@ -55,10 +68,10 @@ export class PluginRegistry {

register(plugin: IPlugin) {
const pluginName = plugin.name;
if (this.#markedPlugin.has(pluginName)) {
if (this.plugins.has(pluginName)) {
throw new Error(`duplicated plugin: ${pluginName}`);
}
this.#markedPlugin.add(pluginName);
this.plugins.set(pluginName, plugin);

for (const name of allowHookName) {
// @ts-ignore

Check warning on line 77 in packages/blocky-core/src/registry/pluginRegistry.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Do not use "@ts-ignore" because it alters compilation errors
Expand All @@ -68,18 +81,21 @@ export class PluginRegistry {
}
}

private genEmit = (hookName: string) =>
((...args: any[]) => {
const plugins = this.#hook[hookName]!;
for (const plugin of plugins) {
try {
// @ts-ignore
plugin[hookName]!(...args);
} catch (e) {
console.error(e);
}
}
}) as any;
unload(name: string) {
this.plugins.delete(name);

emitInitPlugins: (editor: Editor) => void = this.genEmit(hookOnInitialized);
const context = this.contexts.get(name);
if (context) {
context.dispose();
this.contexts.delete(name);
}
}

initAllPlugins(editor: Editor) {
for (const plugin of this.plugins.values()) {
const context = new PluginContext(editor);
this.contexts.set(plugin.name, context);
plugin.onInitialized?.(context);
}
}
}
23 changes: 20 additions & 3 deletions packages/blocky-core/src/view/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,21 @@ import { type CollaborativeCursorFactory } from "./collaborativeCursors";
import { Editor } from "./editor";
import { type FollowerWidget } from "./followerWidget";
import type { ThemeData } from "@pkg/model/theme";
import makeStyledTextPlugin from "@pkg/plugins/styledTextPlugin";
import makeCodeTextPlugin from "@pkg/plugins/codeTextPlugin";
import makeBulletListPlugin from "@pkg/plugins/bulletListPlugin";
import makeHeadingsPlugin from "@pkg/plugins/headingsPlugin";
import { isUndefined } from "lodash-es";

export function makeDefaultEditorPlugins(): IPlugin[] {
return [
makeStyledTextPlugin(),
makeCodeTextPlugin(),
makeBulletListPlugin(),
makeHeadingsPlugin(),
];
}

const defaultEmptyContent = "Empty content";

export interface IEditorControllerOptions {
Expand Down Expand Up @@ -164,7 +177,11 @@ export class EditorController {
public options?: IEditorControllerOptions
) {
this.pluginRegistry =
options?.pluginRegistry ?? new PluginRegistry(options?.plugins);
options?.pluginRegistry ??
new PluginRegistry([
...makeDefaultEditorPlugins(),
...(options?.plugins ?? []),
]);
this.spanRegistry = options?.spanRegistry ?? new SpanRegistry();
this.embedRegistry = options?.embedRegistry ?? new EmbedRegistry();
this.blockRegistry = options?.blockRegistry ?? new BlockRegistry();
Expand All @@ -177,11 +194,11 @@ export class EditorController {
divHandler: this.#divHandler,
});

options?.plugins?.forEach((plugin) => {
for (const plugin of this.pluginRegistry.plugins.values()) {
plugin.blocks?.forEach((block) => this.blockRegistry.register(block));
plugin.spans?.forEach((span) => this.spanRegistry.register(span));
plugin.embeds?.forEach((embed) => this.embedRegistry.register(embed));
});
}
this.blockRegistry.seal();
this.spanRegistry.seal();

Expand Down
2 changes: 1 addition & 1 deletion packages/blocky-core/src/view/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export class Editor {

this.disposables.push($on(container, "mouseleave", this.#hideSpanner));

this.registry.plugin.emitInitPlugins(this);
this.registry.plugin.initAllPlugins(this);

this.#renderer = new DocRenderer({
clsPrefix: "blocky",
Expand Down
9 changes: 0 additions & 9 deletions packages/blocky-example/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import {
makeReactToolbar,
type SpannerRenderProps,
} from "blocky-react";
import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin";
import makeCodeTextPlugin from "blocky-core/dist/plugins/codeTextPlugin";
import makeBulletListPlugin from "blocky-core/dist/plugins/bulletListPlugin";
import makeHeadingsPlugin from "blocky-core/dist/plugins/headingsPlugin";
import SearchBox from "@pkg/components/searchBox";
import { makeImageBlockPlugin } from "./plugins/imageBlock";
import { makeCommandPanelPlugin } from "./plugins/commandPanel";
Expand All @@ -21,17 +17,12 @@ import { ReadMeContent } from "./readme";
import { Theme } from "./themeSwitch";
import { isHotkey } from "is-hotkey";
import { Subject, takeUntil } from "rxjs";
import "blocky-core/css/styled-text-plugin.css";
import "blocky-core/css/blocky-core.css";
import "./app.scss";
import Sidebar from "./components/sidebar";

function makeEditorPlugins(): IPlugin[] {
return [
makeStyledTextPlugin(),
makeCodeTextPlugin(),
makeBulletListPlugin(),
makeHeadingsPlugin(),
makeImageBlockPlugin(),
makeCommandPanelPlugin(),
makeAtPanelPlugin(),
Expand Down
12 changes: 2 additions & 10 deletions packages/blocky-example/src/docs/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ import { EditorController } from "blocky-core";
import { makeReactSpanner, makeReactToolbar } from "blocky-react";
import BannerMenu from "./bannerMenu";
import ToolbarMenu from "./toolbarMenu";
import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin";
import makeBulletListPlugin from "blocky-core/dist/plugins/bulletListPlugin";
import makeHeadingsPlugin from "blocky-core/dist/plugins/headingsPlugin";
import "blocky-core/css/styled-text-plugin.css";
import "blocky-core/css/blocky-core.css";

/**
Expand All @@ -51,16 +47,12 @@ function makeController(): EditorController {
/**
* Define the plugins to implement customize features.
*/
plugins: [
makeBoldedTextPlugin(),
makeBulletListPlugin(),
makeImageBlockPlugin(),
],
plugins: [],
/**
* Tell the editor how to render the banner.
* We use a banner written in React here.
*/
bannerFactory: makeReactSpanner((editorController: EditorController) => (
bannerFactory: makeReactBanner((editorController: EditorController) => (
<BannerMenu editorController={editorController} />
)),
/**
Expand Down
8 changes: 0 additions & 8 deletions packages/blocky-example/src/noTitle/noTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import { Component, ReactNode, createRef } from "react";
import Sidebar from "@pkg/components/sidebar";
import { BlockyEditor, makeReactToolbar } from "blocky-react";
import { EditorController, IPlugin } from "blocky-core";
import makeStyledTextPlugin from "blocky-core/dist/plugins/styledTextPlugin";
import makeCodeTextPlugin from "blocky-core/dist/plugins/codeTextPlugin";
import makeBulletListPlugin from "blocky-core/dist/plugins/bulletListPlugin";
import makeHeadingsPlugin from "blocky-core/dist/plugins/headingsPlugin";
import { makeImageBlockPlugin } from "@pkg/plugins/imageBlock";
import { makeCommandPanelPlugin } from "@pkg/plugins/commandPanel";
import { makeAtPanelPlugin } from "@pkg/plugins/atPanel";
Expand All @@ -14,10 +10,6 @@ import { timer, Subject, takeUntil } from "rxjs";

function makeEditorPlugins(): IPlugin[] {
return [
makeStyledTextPlugin(),
makeCodeTextPlugin(),
makeBulletListPlugin(),
makeHeadingsPlugin(),
makeImageBlockPlugin(),
makeCommandPanelPlugin(),
makeAtPanelPlugin(),
Expand Down
7 changes: 5 additions & 2 deletions packages/blocky-example/src/plugins/atPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
TextBlock,
Embed,
type EmbedInitOptions,
type PluginContext,
} from "blocky-core";
import { makePreactFollowerWidget } from "blocky-react";
import { Delta } from "blocky-data";
import { takeUntil } from "rxjs";
import "./atPanel.scss";

interface AtPanelProps {
Expand Down Expand Up @@ -71,8 +73,9 @@ export function makeAtPanelPlugin(): IPlugin {
return {
name: "at-panel",
embeds: [MyEmbed],
onInitialized(editor) {
editor.keyDown.subscribe((e: KeyboardEvent) => {
onInitialized(context: PluginContext) {
const { editor, dispose$ } = context;
editor.keyDown.pipe(takeUntil(dispose$)).subscribe((e: KeyboardEvent) => {
if (e.key !== "@") {
return;
}
Expand Down
Loading

1 comment on commit c8c6f0c

@vercel
Copy link

@vercel vercel bot commented on c8c6f0c Nov 12, 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.