I’m currently experimenting with the new constraints system and am trying to create a model for a nestable todo list. The goal is to support the same operations as bullet lists (e.g. wrapping multiple paragraphs) and list items (sink and lift).
This is the code I have so far (I am testing it in demo.js):
var model = require("../dist/model");
var InputRule = require("../dist/inputrules").InputRule
class TodoList extends model.Block {}
TodoList.prototype.serializeDOM = (node, s) => s.renderAs(node, "ul", {class: 'task-list'})
TodoList.register('autoInput', 'insert', new InputRule(
/^task $/,
' ',
function(pm, match, pos) {
const todoItem = pm.schema.node("task_item", {}, [ pm.schema.node("checkbox"), pm.schema.node("paragraph")])
const todoList = this.create({}, todoItem);
const $pos = pm.doc.resolve(pos);
pm.tr.replaceWith($pos.before($pos.depth), $pos.after($pos.depth), todoList)
.apply(pm.apply.scroll);
}
));
class TodoItem extends model.ListItem {}
TodoItem.prototype.serializeDOM = (node, s) => s.renderAs(node, "li", {class: 'task-item'})
class Checkbox extends model.Inline {
get attrs() { return { done: new model.Attribute({default: false}) } }
}
Checkbox.prototype.serializeDOM = (node, s) => {
return s.elt('input', {
type: 'checkbox',
checked: node.attrs.done ? '' : undefined,
});
}
const schema = new model.Schema({
nodes: {
doc: {type: model.Doc, content: "block+"},
text: {type: model.Text},
paragraph: {type: model.Paragraph, content: "inline*"},
task_list: {type: TodoList, content: "task_item+"},
task_item: {type: TodoItem, content: "checkbox paragraph task_list?"},
checkbox: {type: Checkbox},
},
groups: {
block: ["paragraph", "task_list"],
inline: ["text"]
},
})
So far the following questions have come up:
- Trying to simply use
this.create({})
inside theTodoList
input rule does not work, which is why I explicitly create the required child node. At first I expected the system to automatically create the required content as defined in the schema using default attributes. Would it be possible/desired to do this automatically? It’s not a big problem in this case, but might be helpful for other generic commands (maybe splitting). - Creating a
TodoList
using the input rule when there is no other content correctly places the cursor in the paragraph of the list item. Doing the same thing when there is a paragraph below, the cursor is placed in the paragraph below the createdTodoList
. I suppose this is the wanted default behavior when creating block nodes, right? In this case I would want the cursor to be positioned in the paragraph of theTodoItem
. How is this handled inBulletLists
? - Trying to indent the
TodoItem
(which extendsListItem
) throws anUncaught RangeError: Wrap not possible
. I hoped that this would work without adjustments as the structure is exactly the same as with aBulletList
andListItems
. (To try this, I created two empty paragraphs, created two TodoLists using the input rule, joined the two lists, and pressedCtrl+]
inside the secondTodoItem
). - How is it possible to delete such a
TodoItem
? The constraints correctly enforce keeping aCheckbox
andParagraph
node. But I would still like to allow the deletion of such an item using backspace when in front of the checkbox. Is creating a customBackspace
handler the right approach? - How would splitting a
TodoItem
be implemented? I would like to append a newTodoItem
to theTodoList
similar to the behavior ofListItem
. I suppose that creating a customEnter
handler forTodoItems
is the way to go?
Ideally, I would like to share as much logic as possible with the BulletList
and ListItem
and to derive all the nestable/wrappable/splittable properties they have. I would also be happy about feedback on the overall structure of the model. Please let me know if there is a better way to approach this.