Schema for checkboxes / Trigger command from click on Node

I’m currently working on integrating checkboxes into the editor and implemented a “toggle” command which works fine when triggered via the menu, but I’m not sure how to trigger it from a click on the checkbox by the user. It would be great to get general feedback on my current approach and some help regarding this problem. Maybe there is a better approach to implement this kind of functionality? This is the first time I’m trying to extend the schema.

This is what I have so far:

Schema:

export class Task extends Inline {}
Task.attributes = {
    done: new Attribute({default: false})
}

Commands:

Task.attachCommand("insertTask", type => ({
  label: "Insert task",
  run(pm, done) {
    let sel = pm.selection, tr = pm.tr
    return pm.apply(tr.insertInline(sel.from, type.create({done})))
  },
  select(pm) {
    return pm.doc.path(pm.selection.from.path).type.canContainType(type)
  },
  menuGroup: "inline",
  menuRank: 40,
}))

defineCommand("toggle", {
  label: "Toggle task",
  run(pm) {
    let taskNodePath = pm.selection.from.path.concat(0)
    let taskNode = pm.doc.path(taskNodePath)
    let replacement = taskNode.type.create({done: !taskNode.attrs.done})
    pm.tr.replaceWith(new Pos(pm.selection.from.path, 0), new Pos(pm.selection.from.path, 1), replacement).apply()
  },
  menuGroup: 'inline'
})

Dom serialization:

def(Task, node => {
  let dom = elt("input")
  dom.setAttribute("type", "checkbox")
  if (node.attrs.done)
    dom.setAttribute("checked", "");

  dom.addEventListener("click", (e) => {
    // What to do here? Would like to trigger the 'toggle' command
  })

  return dom
})

This is a usecase I hadn’t considered yet. In general, mouse interaction with nodes is quite in its infancy, but something near the top of my list (though there’s a difficult item above it – tables).

There is a protocol for selecting nodes based on a click (see clicked in edit/selection.js). Maybe we could extend that to give nodes a chance to react to clicks – i.e., pass it the prosemirror instance as well, and make it possible for the clicked method to signal that it has handled the click by returning true.

Do you have time to implement that?

Sounds good! I might be able to work on it in January. EDIT: I just saw that you already pushed the necessary changes. Thanks a lot!

Have you thought about mouse interactions that would allow drag and drop of nodes? I think it would be a really cool and unique feature to allow the reordering of bullet items or paragraphs by dragging and dropping them inside the editor.

Marijn’s reply gave me a way to handle click events. I want to be able to click on a non-editable widget and edit its attributes. I made edit/input/selectClickedNode visible and was able to make this functional:

function defParamsClick(type) {
    type.prototype.handleClick = (pm, e, path, node) => {
        let menu = pm.mod.menuBar.menu
        let cmd = pm.commands["insert"+type.name]
        if (menu && cmd) {
            selectClickedNode(pm,e)
            menu.enter(readParams(cmd))
            return true;
        } else
            return false;
    }
}

defParamsClick(Scale)
defParamsClick(Textfield)
defParamsClick(Textarea)
defParamsClick(Checkbox)
defParamsClick(Select)

Yaa!

Would being able to drag/drop node selections be useful? I noticed you can’t yet do that, because the browser’s dragstart doesn’t kick in when you start dragging in a selected node, but that’s something I’ll want to wire up (#127).

What about accessibility?

Yes, that would definitely be helpful! To cover all usecases that I have in mind, I would also need a way to get a NodeSelection of a node that the cursor is hovering above and a NodeSelection of where a drop is triggered. Ideally the API would expose three NodeSelections covering ondragstart, ondragover, and ondrop. A specialized library for React, that handles drag and drop pretty well is React DnD. It might be interesting to have a look at it.

I could imagine an API, where Nodes can define onDragStart, onDragOver, and onDrop functions, which all receive the “dragged” NodeSelection as well as the “hovered” or “dropped” NodeSelection. It might also be nice to be able to explicitly set the “draggable” html attribute of nodes.

What about it? I mean, what, specifically, are you worried about?

Sorry, I should have been more explicit. I suppose if you provide keyboard equivalents and all the aria stuff it might be ok. We have found dnd complex to implement in an accessible way.

Right, if the drag/drop does something that isn’t possible in another way, that’s an issue. This seems outside of the scope of ProseMirror though. It’s natively supported drag/drop is just another way to do copy-paste. If sites decide to overload that, they’ll have to provide their own alternatives.