I’m trying a modified version of the Footnotes example and was hoping to get some feedback on my initial implementation before unleashing it on my users.
I’ve added a footnote node and a paragraph node per this excellent thread
const footnoteNodeSpec = {
type: 'footnote',
content: 'block+',
group: 'inline',
inline: true,
defining: true,
attrs: {
expanded: {
default: true
}
},
parseDOM: [{
tag: "footnote",
getAttrs(dom) {
return {
expanded: dom.classList.contains("expanded")
}
}
}],
toDOM(node) {
return ["footnote", {
"class": node.attrs.expanded ? "expanded" : "collapsed",
"title": "footnote"
}, ["content", 0]]
}
}
const paragraphNodeSpec = {
content: 'inline*',
group: 'block',
parseDOM: [
{tag: 'paragraph'},
{tag: 'p'}
],
toDOM() {
return ['paragraph', 0];
}
}
along with some basic styling:
.ProseMirror {
counter-reset: prosemirror-footnote;
}
footnote {
display: inline-block;
vertical-align: text-top;
background: #efefef;
max-width: 60%;
min-width: 1em;
min-height: 1em;
margin: 0 0.25em;
}
footnote footnotehead::before {
content: "Footnote " counter(prosemirror-footnote);
vertical-align: super;
font-size: 75%;
padding: 0 0.25em;
counter-increment: prosemirror-footnote;
}
footnote.collapsed content {
display: none;
}
footnote.expanded content {
display: block;
min-height: 1.1em;
}
paragraph {
display: block;
padding: 6px 12px;
}
and a custom NodeView
class FootnoteNodeView {
constructor(node, view, getPos) {
this.dom = window.document.createElement('footnote')
if (node.attrs.expanded) {
this.dom.classList.add('expanded')
}
else {
this.dom.classList.add('collapsed')
this.dom.contentEditable = false
}
let head = this.dom.appendChild(window.document.createElement('footnotehead'))
head.contentEditable = false
this.contentDOM = this.dom.appendChild(window.document.createElement('content'))
head.addEventListener("click", e => {
if (node.attrs.expanded) {
view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, {expanded: false}))
}
else {
view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, {expanded: true}))
}
e.preventDefault()
})
}
}
The biggest deviation from the example code is that I’ve traded the extra PM instance and management code for an expanded
attribute on the footnote node – which feels janky, but works a treat in my testing. I may eventually have to keep track of the footnote id to better ease displaying them at the bottom of rendered documents with links between their actual location and their displayed content. I’m assuming that’ll involve a Plugin that looks for footnotes in transactions and updates their order as needed, but I’m saving that piece for after my initial implementation is proven sound. Any feedback on any of this is much appreciated.