How to insert linebreaks and formatting in footnotes


#1

I am trying to implement ProseMirror footnotes. I have followed the example at http://prosemirror.net/examples/footnote/ and I have it working. The editor shows up, I can write a single unformatted footnote, serialize it, and reload it. I can also use the formatting commands from the outer editor to apply formatting to the entire footnote, e.g. italicize the entire contents or bold the entire contents. However, I am stumped at how to

  1. insert linebreaks
  2. allow formatting of parts of the footnotes, e.g. italicize some words and not others

I suspect that either I am close, and just need to pass correct arguments to the key functions, or I am more fundamentally misunderstanding how the example sets up the footnote node and the example given does not allow for multiple runs within the footnote node.

I have been trying to add more keybindings to the keymap function in the footnoteView class described in the example:

I tried to add this for linebreaks. I have passed it various arguments that I believed might be the correct target, but none have worked "

Enter": () => splitListItem(schema.nodes.list_item),

I tried to add this for italics. again, I have not been able to figure out the correct arguments

‘Mod-i’: () => toggleMark(schema.marks.em)

open() {
    // Append a tooltip to the outer node
    let tooltip = this.dom.appendChild(document.createElement("div"))
    tooltip.className = "footnote-tooltip"
    // And put a sub-ProseMirror into that
    this.innerView = new EditorView(tooltip, {
      // You can use any node as an editor document
      state: EditorState.create({
        doc: this.node,
        plugins: [keymap({
          "Mod-z": () => undo(this.outerView.state, this.outerView.dispatch),
          "Mod-y": () => redo(this.outerView.state, this.outerView.dispatch),
//I tried to add this for linebreaks. I have passed it various arguments that I believed might be the correct target, but none have worked
"Enter": () => splitListItem(schema.nodes.list_item),
//I tried to add this for italics. again, I have not been able to figure out the correct arguments
'Mod-i': () => toggleMark(schema.marks.em)
        })]
      }),
      // This is the magic part
      dispatchTransaction: this.dispatchInner.bind(this),
      handleDOMEvents: {
        mousedown: () => {
          // Kludge to prevent issues due to the fact that the whole
          // footnote is node-selected (and thus DOM-selected) when
          // the parent editor is focused.
          if (this.outerView.hasFocus()) this.innerView.focus()
        }
      }
    })
  }

I have read through the documents and the posts here on discuss that mentioned ‘footnotes’ or ‘nesting’ and I have not found anything that I could use to solve this issue. If anyone can give me the correct arguments for the commands or point me to the part of the documentation that will help me figure out how to do it I’d appreciate it.

Thanks.


#2

Do you mean you want the footnote to be able to hold multiple paragraphs? If so, you’ll have to change your schema to allow different content inside of them (the example just allows inline nodes).

That should be possible with the code you show—what happens when toggleMark runs on the footnote’s editor?


#3

This is correct. Changing the footnote spec content to “paragraph+” allows linebreaks within footnotes. Thank you for that answer. However, I find that I can only insert the linebreaks by copying and pasting. Hitting the enter key does not seem to have any effect. It feels to me like I am passing the wrong arguments to splitListItem(schema.nodes.list_item), because I know the function is getting invoked when enter is hit. Console.log (‘you hit enter’) works just fine, for example.

There is no observable effect. Nothing in the display changes, and I cannot see any action being dispatched. For example, changing it to

"Mod-i": () => {
              console.log("I hear you want italics");
              toggleMark(schema.marks.em)
            }

and hitting mod-i logs “I hear you want italics” to the console, but nothing gets italicized, I don’t see any editor transaction get dispatched, and the observable editorState does not change. This is unlike when I am in the outer editor. In the outer editor when I hit mod-i the editorState storedMarks changes to [{type:‘em’}] . If it’s not a matter of incorrect arguments, is it perhaps that these keymaps should be declared in a different place?

Thanks.

Edit: I thought perhaps I wasn’t telling the inner editor the correct dispatch, but if that were the case then typing wouldn’t work at all. I changed mod-i to () => this.outerView.dispatch(toggleMark(schema.marks.em))

which did work to italicize it but then it immediately crashed, saying

TypeError: Cannot read property 'eq' of undefined
```
1041 | EditorState.prototype.applyInner = function applyInner(tr) {  
1042 |   var this$1 = this;  
1043 | > 
1044 |   if (!tr.before.eq(this.doc)) {  
1045 |     throw new RangeError("Applying a mismatched transaction");  
1046 |   }  
1047 | 
```

Still, feels like progress. As I currently understand it, I need to figure out how to correctly dispatch these actions so I don’t crash the editor.


#4

Ah, now I see what’s going on. You’re binding the keys incorrectly

  'Mod-i': () => toggleMark(schema.marks.em)

Should be:

  'Mod-i': toggleMark(schema.marks.em)

I.e. toggleMark returns a command function, which should be the direct value of the binding (and thus get the parameters passed to the command by the keymap code)


#5

You are correct. Changing the key bindings fixed it.

Thanks again!