I’m working on a simple footnote number node that shows a bracketed, non-editable value i.e. [1] or [37]. I created a schema based on this footnote example :
const noteFlagSpec = {
group: "inline",
content: "text*",
inline: true,
draggable: true,
selectable: true,
attrs: {
id: {default: null},
value: {default: "[1]"}
},
atom: true,
toDOM: node => {
let ele = document.createElement("footnote")
ele.textContent = node.attrs.value
return ele
},
parseDOM: [{tag: "footnote"}]
}
I also created a node view because my understanding is this is necessary to make it non-editable, even when clicking and keyboarding into the node.
export class NoteFlagView {
constructor(node, view, getPos) {
this.dom = document.createElement("footnote")
}
selectNode() {
this.dom.classList.add("ProseMirror-selectednode")
}
deselectNode() {
this.dom.classList.remove("ProseMirror-selectednode")
}
ignoreMutation() { return true }
}
This code does almost exactly what I want, except for one case: when creating a selection the node does not appear highlighted like the rest of the inline text.
Is this because I overrode selectNode? Did I overdo it with the overrides?
I ended up scrapping my view and doing this with the node spec and a plugin.
const noteFlagSpec = {
group: "inline",
content: "inline*",
inline: true,
draggable: true,
selectable: true,
attrs: {
id: {default: null},
type: {default: "number"},
value: {default: 1},
inSelection: {default: false}
},
atom: true,
toDOM: node => {
let ele = document.createElement("footnote")
ele.textContent = '[' + node.attrs.value +']'
if (node.attrs.inSelection) {
ele.classList.add("node-in-selection")
} else {
ele.classList.remove("node-in-selection")
}
return ele
},
parseDOM: [{tag: "footnote"}]
}
export const noteFlagPlugin = new Plugin({
appendTransaction: function(transactions, oldState, newState) {
var {ranges} = newState.selection
var noteFlagVal = 1
var tr = newState.tr
newState.doc.descendants((node, pos) => {
if (node.type.name === 'footnote') {
// If this node is inside a text selection mark it selected
let inSel = false
for (var i = 0; i < ranges.length; i++) {
if (ranges[i].$from.pos <= pos && ranges[i].$to.pos > pos) {
tr.setNodeMarkup(pos, node.type, {inSelection: true, value: noteFlagVal})
inSel = true
}
}
if (!inSel) {
tr.setNodeMarkup(pos, node.type, {inSelection: false, value: noteFlagVal})
}
noteFlagVal += 1
}
});
// setNodeMarkup drops the selection so re-select whatever was selected
// to begin with
tr.setSelection(newState.selection)
return tr
}
})
I override the browser selection color because it’s apparently not possible to get this information using Javascript. CSS:
::selection {
background: #99def7; /* WebKit/Blink Browsers */
}
::-moz-selection {
background: #99def7; /* Gecko Browsers */
}
.node-in-selection {
background: #99def7;
}
1 Like
colel
June 23, 2020, 2:16pm
3
Thank you for sharing!
I have a similar problem, but I want to try a combination of your two approaches.