I’ve been working on integrating a NodeView into my ProseMirror-based document editor and encountered several cursor behavior issues that I’m struggling to understand. I’m hoping to find some solutions or insights.
My code is as follows:
import { EditorState } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Schema, DOMParser } from 'prosemirror-model';
import { schema as basicSchema } from 'prosemirror-schema-basic';
import { addListNodes } from 'prosemirror-schema-list';
import { exampleSetup } from 'prosemirror-example-setup';
const editableNode = {
inline: true,
content: "inline*",
group: "inline",
selectable: true,
parseDOM: [{
tag: 'span[data-type="editable-node"]',
getAttrs: dom => ({
"data-placeholder": dom.getAttribute("data-placeholder") || ""
})
}],
toDOM(node) {
return ["span", {"data-type": "editable-node", "data-placeholder": node.attrs["data-placeholder"] || ""}, 0];
}
};
const mySchema = new Schema({
nodes: addListNodes(basicSchema.spec.nodes, 'paragraph block*', 'block').append({
editableNode,
}),
marks: basicSchema.spec.marks,
});
class EditableNodeView {
constructor(node, view, getPos) {
this.node = node
this.dom = this.createContainer();
this.contentDOM = this.createContentArea(node);
this.dom.appendChild(this.contentDOM);
}
createContainer() {
const dom = document.createElement("span");
dom.setAttribute("data-type", "editable-node");
dom.style.border = "1px solid gray";
dom.style.marginLeft = "2px";
dom.style.marginRight = "2px";
dom.style.borderRadius = "5px";
dom.style.padding = "1px 10px";
return dom;
}
createContentArea(node) {
const contentDOM = document.createElement("span");
contentDOM.contentEditable = true;
contentDOM.setAttribute("data-placeholder","this is placeholder")
contentDOM.innerText = "Every morning, we have the opportunity to embrace a new beginning. The sun rises, casting a golden glow over the world, reminding us that each day holds the promise of fresh starts and new adventures."
return contentDOM;
}
}
window.view = new EditorView(document.querySelector('#editor'), {
state: EditorState.create({
doc: DOMParser.fromSchema(mySchema).parse(document.querySelector('#content')),
plugins: exampleSetup({ schema: mySchema }),
}),
nodeViews: {
editableNode(node, view, getPos) {
return new EditableNodeView(node, view, getPos);
},
}
});
function insertEditableNode(nodeType) {
return (state, dispatch) => {
const { selection } = state;
const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos;
const node = nodeType.create();
const transaction = state.tr.insert(position, node);
if (dispatch) {
dispatch(transaction);
}
return true;
};
}
const insertCommand = insertEditableNode(mySchema.nodes.editableNode);
document.querySelector('#myButton').addEventListener('click', () => {
const { state, dispatch } = window.view;
insertCommand(state, dispatch);
});
<!DOCTYPE html>
<html>
<head>
<title>ProseMirror Editor</title>
<script src="dist/prosemirror-bundle.min.js" defer></script>
<link rel="stylesheet" href="dist/editor.css">
</head>
<style>
/* CSS 规则 */
[data-type="editable-node"] .empty::before {
content: attr(data-placeholder); /* 使用 data-placeholder 属性作为内容 */
color: #ccc;
font-style: italic;
pointer-events: none;
}
p {
line-height: 2;
}
</style>
<body>
<!--<div id="mad_interactive_editor"></div>-->
<button id="myButton">Insert Input Node</button>
<div id="editor"></div>
<div id="content" style="display: none;">
<p>Hello ProseMirror</p>
<p>This is editable text. You can focus it and start typing.</p>
</div>
</body>
</html>
Here is a summary of the behavior I observed:
- Cursor Jumping to End of NodeView:
- Action: I inserted a NodeView between the letters ‘P’ and ‘r’ in the document text “Hello ProseMirror,” resulting in “Hello P[nodeview]roseMirror.” I set
contentDOM.contentEditable = "true"
, expecting the NodeView’s contentDOM to be editable. - Issue: When I click at the very start of the NodeView, expecting the cursor to be placed to the left of the word “Every,” the cursor instead jumps to the end of the NodeView, right of the word “adventures.”
- Cursor Immobile When Moved Left with Keyboard:
- Issue: If I try to move the cursor left using the keyboard, it doesn’t move.
- Cursor Movement After Typing:
- Action: After typing some content to the right of “adventures.” and then trying to move the cursor left, it then moves as expected.
- Cursor Positioning on Right-End of NodeView:
- Issue: When the cursor is at the right end of the NodeView and I attempt to move it right with the keyboard, I expect it to be between the NodeView and “roseMirror.” However, it ends up between ‘r’ and ‘oseMirror.’ Moving the cursor left then correctly places it between the NodeView and “roseMirror.” Further left movement doesn’t go to the expected end of the NodeView but to the left of the last letter in the NodeView.
- Cursor Positioning on Left-End of NodeView:
- Issue: When the cursor is at the left end of the NodeView and I attempt to move it left with the keyboard, I expect it to be between the NodeView and “P.” However, it ends up between 'Hello ’ and ‘P’. Moving the cursor right then correctly places it between the NodeView and “P.” Further right movement doesn’t go to the expected start of the NodeView but to the right of the first letter in the NodeView.
I observed these issues in both Chrome and Firefox browsers. The version of ProseMirror I am using is:
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"prosemirror-model": "^1.19.3",
"prosemirror-schema-basic": "^1.2.2",
"prosemirror-state": "^1.4.3",
"prosemirror-transform": "^1.8.0",
"prosemirror-view": "^1.32.5"
}
Could you please provide any insights or guidance on why these issues are occurring and how they might be resolved?
Thank you for your assistance.