Hello,
How would one go about getting the absolute position of a mark when the mouse is not focused or selected on that tag? Is this possible with Prosemirror?
For more context as to the specific use case of this… I have a custom schema that tacks on a guid as well as some other attributes to the mark. These are then rendered in a component that is separate from the editor. From this rendered component, I would like to be able to delete/update the marks and have that be reflected in the schema presented by the editor, but I would need to know their absolute positions in the editor so I know which parts of the schema need to be updated.
I would hope that one of the ways listed below would be a valid way to go about getting the absolute position of a given mark, but I’m not sure if this is something Prosemirror is capable of.
Way 1:
const mark = document.getElementById(this.guid);
// get the to and from positions off of the constant mark.
Way 2:
Within the schema, storing the absolute position on a data attribute. (I could save this whenever a mark is added since I am toggling marks on selected ranges, but that wouldn’t account for copying/pasting that mark around the document or editing text around the mark that would ultimately change it’s absolute position)
get schema(): MarkSpec {
return {
attrs: {
attributes: {}
},
toDOM: (node: any) => [
'mark',
{
'data-value': JSON.parse(node.attrs.attributes).value,
id: JSON.parse(node.attrs.attributes).guid,
from: // Somehow get the from selection,
to: // Somehow get the to selection
}
],
parseDOM: [
{
...
...
...
}
]
};
}
The absolute position from and to are document dependent, and I would caution against saving those in the schema as done in way-2. This adds unnecessary complexity and will be much hard to maintain when the positions become stale.
I am not sure what the most pragmatic approach will be, but here is my attempt:
// uid is a string which uniquely identifies the mark you are looking for
export function findMarkByUid(editorState, from, to, uid) {
let found;
editorState.doc.nodesBetween(from, to, (node) => {
if (found) return false;
found = node.marks.find(mark => mark.attrs['data-uid'] === uid)
});
return found
}
// somewhere in your mark schema
toDOM: () => ['em', {'data-uid': generateUID()}, 0],
If you want the document wide positions, you can do something like:
function findMarkPosition(mark, doc, from, to) {
let markPos = { start: -1, end: -1 };
doc.nodesBetween(from, to, (node, pos) => {
// stop recursing if result is found
if (markPos.start > -1) {
return false;
}
if (markPos.start === -1 && mark.isInSet(node.marks)) {
// expect to see something like `italic('my text')`
console.log(node.toString())
markPos = {
start: pos,
end: pos + Math.max(node.textContent.length, 1),
};
}
});
return markPos;
}
Hello, have you ever come up with the solution.
I have a very fancy scenario. To oversimplify: I want to delete a mark on click. And just to add I neither not have a selection in the editor not the cursor
I used markView to add the ‘click’ event on the mark element
and then I thought I can use Mark instance and be sure it will be equal to exact mark in the document.
export class MarkPopupViewBuilder implements ReturnType<MarkViewConstructor> {
public dom: HTMLElement;
public mark: Mark;
public view: EditorView;
public inline: boolean;
constructor(mark: Mark, view: EditorView, inline: boolean) {
this.mark = mark;
this.view = view;
this.inline = inline;
if (mark.type.spec.toDOM) {
const { dom } = DOMSerializer.renderSpec(document, mark.type.spec.toDOM(mark, inline), null);
this.dom = dom as HTMLElement;
}
this.dom.addEventListener('click', () => {
const { state, dispatch } = this.view;
const tr = state.tr;
state.doc.nodesBetween(0, state.doc.content.size, (node, pos) => {
if (mark.isInSet(node.marks)) { //Look this line <-------------------------------------------
tr.removeMark(pos, pos + node.nodeSize, mark.type);
}
});
dispatch(tr);
} );
}
}
But apparently it’s not, if mark doesn’t have any special attributes it will match all such marks in the document.
I am looking for a way how can I get the position of this mark in the text and toggle/remove it being sure no other locations in the document will be affected.
So far only the solution with custom id attribute I have but it begins messy when we deal with ctrl-c+v