I’m building some complex custom NodeViews that have controls and nested Content DOMs. To determine whether the selection is inside the node in a NodeView, i.e. the user is currently typing in that NodeViews Content DOM I am using the getPos function and checking if the selection is between getPos() and getPos() + node.nodeSize:
I’m trying to build a MarkView to render a floating panel with controls to edit the props on the mark. The floating panel should appear if the selection is currently inside the Mark. Without the getPos function I don’t know how I would tell if the selection is inside the mark?
I’ve tried using a NodeView and making the mark an inline Node but that causes issues with cursor positioning (the edges of the node are valid cursor positions) and the backspace key (hitting backspace just outside the node causes the node to be deleted rather than moving into the node and deleting the last character of the nodes content)
You didn’t really ask a very clear question, but the problem may revolve around the fact that a node view doesn’t really get to see arbitrary updates to the document, so there’s no good point to run the code you pasted. It might be easier to drive this panel from a plugin, which can test whether the selection is inside a node of the relevant type, and adds or removes the panel (either overlaid on the editor or via a widget decoration) as appropriate.
Marks aren’t ‘objects’, in that they are metadata applied to groups of other nodes, but their actual start and end isn’t really meaningful, and they may be split or merged at arbitrary points in time. As such, making them behave like stateful objects in the view is tricky and doesn’t seem like a good conceptual fit to begin with.
My specific problem is that when using a MarkView, I don’t have any clue at which position it is rendered in the document (because of a missing getPos). This makes it impossible to update its attrs.
Okay I found a basic way to calculate a mark view range. I can get the start position with posAtDOM and the dom element. After that I have to get the full mark range within the parent node.
getMarkPos() {
const pos = this.view.posAtDOM(this.dom)
const resolvedPos = this.view.state.doc.resolve(pos)
const range = getMarkRange(resolvedPos, this.node.type)
return range
}
getMarkRange($pos = null, type = null) {
if (!$pos || !type) {
return false
}
const start = $pos.parent.childAfter($pos.parentOffset)
if (!start.node) {
return false
}
const link = start.node.marks.find(mark => mark.type === type)
if (!link) {
return false
}
let startIndex = $pos.index()
let startPos = $pos.start() + start.offset
let endIndex = startIndex + 1
let endPos = startPos + start.node.nodeSize
while (startIndex > 0 && link.isInSet($pos.parent.child(startIndex - 1).marks)) {
startIndex -= 1
startPos -= $pos.parent.child(startIndex).nodeSize
}
while (endIndex < $pos.parent.childCount && link.isInSet($pos.parent.child(endIndex).marks)) {
endPos += $pos.parent.child(endIndex).nodeSize
endIndex += 1
}
return { from: startPos, to: endPos }
}