How to create custom node with two inputs

Hi guys, is it possible to create a custom node which looks like this: custom block

It has two fields, one for theorem name and the second one for theorem body. I would love to simply press ctrl-shift-T and get empty block like this.

If its possible, could you please give me some tips or show me similar examples so that I could see the code and inspire myself? I know that I have to write Schema for this node but it has two inputs and I don’t know how to do it.

You should be able to do this by defining a block node type with two required childen (using either plain paragraphs, or some other specialized node type for the children). I.e. content: "theorem_name theorem_body".

I did the following:

and the results looks good

but its not editable. I would like to be able to put there anything. What should I change to make it work and possibly to make the code written nicer?

Here are my CSS styles:

.theorem_block {

border-radius: 5px;

box-shadow: 8px 8px 24px 0px rgba(66, 68, 90, 1);

}

.ProseMirror.s-prose div.theorem_name {

margin-bottom: 0px;

}

div .theorem_name {

display: block;

color: white;

background-color: rgb(0, 0, 105);

padding: 5px;

}

div .theorem_body {

padding: 5px;

display: block;

color: black;

background-color: rgb(199, 199, 228);

}

Your toDOM method shouldn’t render the node’s children, but provide a hole where they should be rendered (return ["div", {class: "..."}, 0]).

I changed it in the way you said and here is my function to create theorem block:

function insertTheoremBlockCommand() {
    return function (state, dispatch) {
        let {$from, $to } = state.selection;
        let tr = state.tr;
        let nodeType = state.schema.nodes.theorem_block.create();
        tr.replaceSelectionWith(nodeType);
 
        let found  = -1
        tr.doc.nodesBetween(Math.min(tr.selection.anchor, tr.selection.head), Math.min(Math.max(tr.selection.anchor, tr.selection.head) + 2 , tr.doc.nodeSize),
         (node, pos) => {
            if (found > -1) return false
            if (node.type.name === "theorem_block") found = pos
        });
        if (found > -1) console.log("there's a node at", found)
        
        tr.setSelection(NodeSelection.create(tr.doc, found));
        dispatch(tr);
        return true;
    };
}


But now when I press ctrl-m (to call this method) its completely empty and only <div class="theorem_block ProseMirror-selectednode" draggable="true"></div> was generated.

I want to create theorem block with “Theorem” text in the header and with just a single line in the body and both should be editable. What should I do?

Use .createAndFill instead to make the content of your new node actually conform to the schema.

1 Like

Thanks! There is one more thing that I would like to do - initialize theorem name with the default “theorem” text. Here is my current Schema

    theorem_block: {
        content: "theorem_name theorem_body",
        group: "block",
        parseDOM: [
            {
                tag: "theorem_block",
            },
        ],
        toDOM() {
            return [
                // "div", {class: "theorem_block"}, ["div", {class: "theorem_name"}, "name... "], ["div", {class: "theorem_body"}, "body..."]
                "div", {class: "theorem_block"}, 0

            ];
        },
    },
    theorem_name: {
        content: "block+",
        group: "block",
        parseDOM: [
            {
                tag: "theorem_name",
            },
        ],
        toDOM() {
            return [
                "theorem_name",
                {
                    class: "theorem_name",
                },
                0
            ];
        },
    },
    theorem_body: {
        content: "block+",
        group: "block",
        parseDOM: [
            {
                tag: "theorem_body",
            },
        ],
        toDOM() {
            return [
                "theorem_body",
                {
                    class: "theorem_body",
                },
                0
            ];
        },
    },

and here is the output:

How can I force it to be initialized with default “Theorem” text? I.e.

Anyone has any ideas? Currently I think that I should create custom view for this node but maybe it’s possible to do this with toDom() method?

You’re going to have to learn the library—we’re not going to build your whole product for you here on the forum. In this case, you again need to look at how you’re initializing the node you’re inserting. createAndFill will add the minimum content necessary to satisfy the schema. But it sounds like you want to create a specific set of child nodes instead.