Hey Guys!
I’m really new to prose-mirror and still trying to get my head around some of the concepts. I’m trying to create a simple script editor for comic books, so the editor will be highly structured/opinionated. It will have a list of pages, pages have panels, panels have descriptions and speech bubbles…
My issue is when hitting enter, if the editor is currently at a page description (the starting point) it should create a nested panel description and place the cursor there, but it is then creating a second page and moving the cursor to the second page.
I may be doing some other things wrong as well here, any help or advice would be greatly appreciated!
this is everything I have at the moment.
import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {Schema, DOMParser, Fragment} from "prosemirror-model"
import {keymap} from "prosemirror-keymap"
// schema
const scriptSchema = new Schema({
nodes: {
doc: {content: "script"},
script: {
content: "page+",
toDOM(){return ["ol",{class: 'script'}, 0]},
parseDOM: [{tag: "script"}]
},
page: {
content: "pg_description panel_ls",
parseDOM: [{tag: "page"}],
toDOM() {return ['li', 0]}
},
panel_ls: {
content: "panel*",
toDOM() {return ['ol', {class: 'panel_ls'}, 0]}
},
panel: {
content: "pl_description bubble*" ,
parseDOM: [{tag: "panel"}],
toDOM() { return['li', 0] }
},
pg_description: {
content: "text*",
parseDOM: [{tag: "pg_desc"}],
toDOM() {return ['span',{class: 'desc pg_desc'}, 0]}
},
pl_description: {
content: "text*",
parseDOM: [{tag: "pl_desc"}],
toDOM() {return ['span',{class: 'desc pl_desc'}, 0]}
},
bubble: {
parseDOM: [{tag: "bubble"}],
contents: "name speech",
toDOM() {return ['div',{class: 'bubble'}, 0]}
},
name: {
content: "text*",
parseDOM: [{tag: "bubble_name"}],
toDOM() {return ['div',{class: 'bubble bubble_name'}, 0]}
},
speech: {
content: "text*",
parseDOM: [{tag: "bubble_speech"}],
toDOM() {return ['div',{class: 'bubble bubble_speech'}, 0]}
},
text: {}
}
});
const NodeNames = {
script: 'script',
page: 'page',
panel: 'panel',
pg_description: 'pg_description',
pl_description: 'pl_description',
bubble: 'bubble',
name: 'name',
speech: 'speech',
text: 'text',
panel_ls: 'panel_ls'
}
// state map to get the next state from the current
const stateEnter = {
[NodeNames.pg_description]: NodeNames.pl_description,
[NodeNames.pl_description]: NodeNames.name,
[NodeNames.name]: NodeNames.speech,
[NodeNames.speech]: NodeNames.name
}
// key map command
function enterCommand(oState, fnDispatch) {
console.log('Enter command');
// return;
// get the current node type
const cur = oState.selection.$anchor.parent.type.name;
console.log('CURRENT NODE : ' + cur);
let next = stateEnter[cur]; // get the name of the node to go to
// create the new node and insert
// let desc = oState.doc.type.schema.nodes['description'].create();
// let oNode = oState.doc.type.schema.nodes['panel'].createAndFill(desc);
let oNode_ls = nodeFactory()[next](oState, cur);
let oTransaction = oState.tr.replaceSelectionWith(oNode_ls[0]).scrollIntoView();
// let oTransaction = oState.tr.replaceSelectionWith(oNode_ls).scrollIntoView();
oTransaction.setMeta('nodeState', next);
fnDispatch(oTransaction);
// return true;
function nodeFactory() {
return {
[NodeNames.pg_description]: function(oState) {
// let desc = oState.doc.type.schema.nodes['description'].create();
// let oNode = oState.doc.type.schema.nodes['panel'].createAndFill(desc);
// return oNode
throw new Error('Illegal state');
},
[NodeNames.pl_description]: function(oState) {
const desc = oState.doc.type.schema.nodes[NodeNames.pl_description].create();
const panel = oState.doc.type.schema.nodes[NodeNames.panel].createAndFill(desc);
const panel_ls = oState.doc.type.schema.nodes[NodeNames.panel_ls].createAndFill(panel);
return [desc, panel, panel_ls];
// return [panel_ls, panel, desc];
// return desc;
},
[NodeNames.name]: function(oState, cur) {
let oNode;
if (cur === NodeNames.pl_description) {
const name = oState.doc.type.schema.nodes[NodeNames.name].create();
oNode = oState.doc.type.schema.nodes[NodeNames.bubble].createAndFill(name);
} else if (cur === NodeNames.speech) {
const name = oState.doc.type.schema.nodes[NodeNames.name].create();
oNode = oState.doc.type.schema.nodes[NodeNames.bubble].createAndFill(name);
} else {
throw new Error('Illegal State');
}
// let oNode = oState.doc.type.schema.nodes['bubble'].create();
return oNode
},
[NodeNames.speech]: function(oState) {
return oState.doc.type.schema.nodes[NodeNames.speech].create();
}
}
}
}
// main
window.view = new EditorView(document.querySelector("#editor"), {
state: EditorState.create({
doc: DOMParser.fromSchema(scriptSchema).parse(document.querySelector("#content")),
plugins: [keymap({'Enter': enterCommand})]
})
})