@milkdown/plugin-listener
Version:
141 lines (140 loc) • 3.94 kB
JavaScript
import { EditorViewReady, InitReady, SerializerReady, prosePluginsCtx, serializerCtx } from "@milkdown/core";
import { createSlice } from "@milkdown/ctx";
import { Plugin, PluginKey } from "@milkdown/prose/state";
import { debounce } from "lodash-es";
//#region src/index.ts
var ListenerManager = class {
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;
};
}
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
};
}
markdownUpdated(fn) {
this.markdownUpdatedListeners.push(fn);
return this;
}
blur(fn) {
this.blurListeners.push(fn);
return this;
}
focus(fn) {
this.focusListeners.push(fn);
return this;
}
destroy(fn) {
this.destroyListeners.push(fn);
return this;
}
selectionUpdated(fn) {
this.selectionUpdatedListeners.push(fn);
return this;
}
};
var listenerCtx = createSlice(new ListenerManager(), "listener");
var key = new PluginKey("MILKDOWN_LISTENER");
var listener = (ctx) => {
ctx.inject(listenerCtx, new ListenerManager());
return async () => {
await ctx.wait(InitReady);
const { listeners } = ctx.get(listenerCtx);
listeners.beforeMount.forEach((fn) => fn(ctx));
await ctx.wait(SerializerReady);
const serializer = ctx.get(serializerCtx);
let prevDoc = null;
let prevMarkdown = null;
let prevSelection = null;
let latestTr = null;
const debouncedHandler = debounce(() => {
if (!latestTr) return;
const { doc } = latestTr;
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;
latestTr = null;
}, 200);
const plugin = new Plugin({
key,
view: () => {
return { destroy: () => {
debouncedHandler.cancel();
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.storedMarksSet) || tr.getMeta("addToHistory") === false) return;
latestTr = tr;
debouncedHandler();
}
}
});
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"
};
//#endregion
export { ListenerManager, key, listener, listenerCtx };
//# sourceMappingURL=index.js.map