UNPKG

@milkdown/plugin-listener

Version:

The listener plugin of [milkdown](https://milkdown.dev/).

161 lines (160 loc) 5.07 kB
import { InitReady, SerializerReady, serializerCtx, prosePluginsCtx, EditorViewReady } from "@milkdown/core"; import { createSlice } from "@milkdown/ctx"; import { PluginKey, Plugin } from "@milkdown/prose/state"; import { debounce } from "lodash-es"; class ListenerManager { constructor() { this.beforeMountedListeners = []; this.mountedListeners = []; this.updatedListeners = []; this.selectionUpdatedListeners = []; this.markdownUpdatedListeners = []; this.blurListeners = []; this.focusListeners = []; this.destroyListeners = []; this.beforeMount = (fn) => { this.beforeMountedListeners.push(fn); return this; }; this.mounted = (fn) => { this.mountedListeners.push(fn); return this; }; this.updated = (fn) => { this.updatedListeners.push(fn); return this; }; } /// A getter to get all [subscribers](#interface-subscribers). You should not use this method directly. get listeners() { return { beforeMount: this.beforeMountedListeners, mounted: this.mountedListeners, updated: this.updatedListeners, markdownUpdated: this.markdownUpdatedListeners, blur: this.blurListeners, focus: this.focusListeners, destroy: this.destroyListeners, selectionUpdated: this.selectionUpdatedListeners }; } /// Subscribe to the markdownUpdated event. /// This event will be triggered after the editor state is updated and **the document is changed**. /// The second parameter is the current markdown and the third parameter is the previous markdown. markdownUpdated(fn) { this.markdownUpdatedListeners.push(fn); return this; } /// Subscribe to the blur event. /// This event will be triggered when the editor is blurred. blur(fn) { this.blurListeners.push(fn); return this; } /// Subscribe to the focus event. /// This event will be triggered when the editor is focused. focus(fn) { this.focusListeners.push(fn); return this; } /// Subscribe to the destroy event. /// This event will be triggered before the editor is destroyed. destroy(fn) { this.destroyListeners.push(fn); return this; } /// Subscribe to the selectionUpdated event. /// This event will be triggered when the editor selection is updated. selectionUpdated(fn) { this.selectionUpdatedListeners.push(fn); return this; } } const listenerCtx = createSlice( new ListenerManager(), "listener" ); const key = new PluginKey("MILKDOWN_LISTENER"); const listener = (ctx) => { ctx.inject(listenerCtx, new ListenerManager()); return async () => { await ctx.wait(InitReady); const listener2 = ctx.get(listenerCtx); const { listeners } = listener2; listeners.beforeMount.forEach((fn) => fn(ctx)); await ctx.wait(SerializerReady); const serializer = ctx.get(serializerCtx); let prevDoc = null; let prevMarkdown = null; let prevSelection = null; const plugin = new Plugin({ key, view: () => { return { destroy: () => { listeners.destroy.forEach((fn) => fn(ctx)); } }; }, props: { handleDOMEvents: { focus: () => { listeners.focus.forEach((fn) => fn(ctx)); return false; }, blur: () => { listeners.blur.forEach((fn) => fn(ctx)); return false; } } }, state: { init: (_, instance) => { prevDoc = instance.doc; prevMarkdown = serializer(instance.doc); }, apply: (tr) => { const currentSelection = tr.selection; if (!prevSelection && currentSelection || prevSelection && !currentSelection.eq(prevSelection)) { listeners.selectionUpdated.forEach((fn) => { fn(ctx, currentSelection, prevSelection); }); prevSelection = currentSelection; } if (!tr.docChanged || tr.getMeta("addToHistory") === false) return; const handler = debounce(() => { const { doc } = tr; if (listeners.updated.length > 0 && prevDoc && !prevDoc.eq(doc)) { listeners.updated.forEach((fn) => { fn(ctx, doc, prevDoc); }); } if (listeners.markdownUpdated.length > 0 && prevDoc && !prevDoc.eq(doc)) { const markdown = serializer(doc); listeners.markdownUpdated.forEach((fn) => { fn(ctx, markdown, prevMarkdown); }); prevMarkdown = markdown; } prevDoc = doc; }, 200); return handler(); } } }); ctx.update(prosePluginsCtx, (x) => x.concat(plugin)); await ctx.wait(EditorViewReady); listeners.mounted.forEach((fn) => fn(ctx)); }; }; listener.meta = { package: "@milkdown/plugin-listener", displayName: "Listener" }; export { ListenerManager, key, listener, listenerCtx }; //# sourceMappingURL=index.js.map