Widgets and a custom paramHandler

My experiments with building widgets and a customParamHandler are shown here including a link to source. MultipleChoice, Scale and CheckList are definitely a work in progress but form the basis for a Test or Survey schema. Lots of possibilities for ProseMirror.

1 Like

Nice! The ‘insert’ dropdown works well for such a set of nodes. I should probably make it easier to use that, or even make it the default.

Any painful API limitations you ran into?

Nice job! It also demonstrates some general issues with block level widgets: One cannot move beyond them using the arrow keys. If one inserts a widget as the very first item, it’s not possible to move above it to insert text there. Not sure, but these could be contentEditable issues.

Not sure why this doesn’t work in that demo, but if you try it with horizontal rules in my demos (insert them with ctrl-shift-minus), those problems are not present (moving into them selects them, after which you can continue beyond them, and if you select one at the top of the page and press enter, you get a paragraph above it).

There are some enhancements to the paramHandler you might want to make. How would you like me to communicate them (here, patch requests)? I waited because you were thinking of redesigning param handling. I don’t think you need to because the changes I used allow you to:

  • expand the types of input (url, data, regexp text etc.)
  • validate
  • provide modal behavior
  • enable file upload

Your current design also doesn’t allow for both params and a customHandler but that is easily fixed. I really like keeping the params structure but using another paramHandler. All the changes are here

The real pain has been designing a MultipleChoice node. It should work just like a bullet list but have radiobutton decoration. (CheckList has a similar structure but with checkboxes). I am still working on that. It has been helpful in understanding the system.

There are some additional features of ProseMirror that I have found:

  • Instructors can build accessible content without knowing accessibility
  • A survey document schema lets users build a survey without filling out forms
  • Discipline-specific schemas are easily built. For example, English instructors don’t need math tools but do need grammer tools.
  • thinkspace.org supports student teams. Collaborative editing of a team document would be awesome.

Thanks for pointing that out! I could get the selection working by adding

get contains() {
  return null
}

to the block node. It seems that should also work on the block math node in this example.

That is good to know. NodeType contains null but Block contains “block”. What is the significance of block containing itself or just that it is not leaf node?

Sorry, I meant to the node type that extends the block node. So in this case:

class BlockMath extends Block {
    get attrs() {
	return {
		tex: new Attribute({default: ""})
	}
    }
    get contains() {
        return null
    }
}

Yes, contains = null means that it’s a leaf node. If it returns "block" that says that the node can contain anything of type "block", which is definitely not the case here.

Could we fold those two together and just allow the passing in of a validate function? The reason I’m nervous about having a lot of types is that each param handler implementation will have to support them all, which will make it very painful to write new implementations.

I don’t want to do classical modal “you’re trapped in this dialog until we let you out” style, but it’s probably a good idea to autoclose the dialog when the user clicks outside of it. Does that sound reasonable to you?

Yes, those are definitely necessary. I’ll look into adding them.

I am fine with letting the paramHandler developer deal with those issues rather than baking it into PM. There are so many site-specific requirements that it is hard to make it flexible enough in PM. Even the file upload requires hooks to the bigger system.

I ran into two issues in building the paramHandler. In command.exec:

exec(pm, params) {
    let run = this.spec.run
    if (!this.params.length) return run.call(this.self, pm)
    if (params) return run.call(this.self, pm, ...params)
    let handler = getParamHandler(pm)
    if (!handler) return false
    handler(pm, this, params => {
      if (params) run.call(this.self, pm, ...params)
    }) }

If I provides params in my command, I can’t use the params attribute and a paramHandler together.

Secondly, menu.readParams is called from menu.renderIcon and circumvents my param UI. If you can deal with those issues it should work.

See my comments here – I agree the current situation isn’t really working, am interested in suggestions for alternatives.