Hello! I am new to writing ProseMirror plugins and looking for help writing a plugin that does two fold:
- Creates widget decorations (in this case a button) for every header node in the document.
- Creates a view (a menu) whose visibility is controlled by the onClick of the above button and is positioned in relation to the button itself.
I’m struggling to find best approach to link these two together as I can’t figure out how to show/hide the view based on the click of a decoration.
I did take a look at the suggested code from the collab example shown here but that use case seemed a little different and more complicated than what I was trying to accomplish. I also have a fairly strict way of enabling plugins in my project which doesn’t allow me to easily pass dispatch
into the plugin itself.
Here’s my code so far.
import { Plugin, EditorState } from "prosemirror-state";
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
import {
createSectionMenu,
createSectionMenuButton,
} from "prosemirror/Commands/Heading/SectionMenu";
export class SectionMenu {
menu: HTMLElement;
constructor(view: EditorView, parentNode: Node) {
this.menu = createSectionMenu();
parentNode.appendChild(this.menu);
}
show(): void {
this.menu.classList.add("section-menu--show");
}
hide(): void {
this.menu.classList.remove("section-menu--show");
}
setMenuPosition(view: EditorView): void {
// TODO: set position relative to pos of decoration btn
}
}
export default (): Plugin =>
new Plugin({
props: {
decorations: (state: EditorState): DecorationSet => {
const { doc } = state;
const decos: Decoration[] = [];
doc.descendants((node, pos) => {
if (node.type !== state.schema.nodes.heading) {
return false;
}
decos.push(
Decoration.widget(
pos + node.content.size + 1,
createSectionMenuButton(node.attrs.blockID),
{
side: 1,
key: node.attrs.blockID,
}
)
);
return false;
});
return DecorationSet.create(doc, decos);
},
},
view(editorView) {
if (!editorView.dom.parentNode) {
throw new Error("View has no parentNode");
}
return new SectionMenu(editorView, editorView.dom.parentNode);
},
});
export const createSectionMenuButton = (id: string): Node => {
const menuButton = document.createElement("button");
menuButton.classList.add("section-menu__btn");
menuButton.onclick = () => {
console.log("menu button clicked, id: ", id);
// ???
};
return menuButton;
};
Can these exist in the same plugin?
Would appreciate any tips or nudges in the right direction.