-
-
Notifications
You must be signed in to change notification settings - Fork 152
Description
Hi, thank you for your work on this library!
I'm trying to sync scroll positions between the two panes (Original and Modified) in react-codemirror-merge, so I need access to the actual scrollable DOM elements (scrollDOM) of each editor.
❗ Problem
In the @uiw/react-codemirror package, the CodeMirror component provides an onCreateEditor prop where I can easily access the underlying DOM and editor instance. However, in react-codemirror-merge, the CodeMirrorMerge component does not provide this prop.
So instead, I use a ref to get the editor object. I do receive editor.view.a and editor.view.b, but:
- editor.view.dom, editor.view.a.dom, and editor.view.a.scrollDOM do not match the actual DOM rendered in the browser (as seen in the Elements tab).
- I tried logging them after delays (setTimeout, requestAnimationFrame), but they always point to a stripped-down or intermediate structure that does not include .cm-gutters, .cm-layer, or the actual scrollable container.
🔁 Code Example
<CodeMirrorMerge
ref={editorRef}
orientation="a-b"
>
<Original
value={originalContent?.code}
extensions={extensions}
/>
<Modified
value={newContent?.code}
extensions={[
...extensions,
EditorView.editable.of(false),
EditorState.readOnly.of(true),
]}
/>
</CodeMirrorMerge>
const editorRef = useCallback((editor: CodeMirrorMergeRef) => {
if (!editor?.view) return;
// Test 1
console.log(editor.view.dom, 'editor.view.dom');
console.log(editor.view.b.dom, 'editor.view.b.dom');
// Test 2
document.querySelectorAll(".cm-scroller").forEach((node, index) => {
console.log(`Scroller [${index}]:`, node);
});
// Test 3
setTimeout(() => {
console.log(editor.view.dom, 'editor.view.dom in setTimeout');
console.log(editor.view.b.dom, 'editor.view.b.dom in setTimeout');
}, 3000);
}, []);
🧪 Observations
(for all the tests, they return the same result)
From ref:
<div class="cm-mergeView">
<div class="cm-mergeViewEditors">
<div class="cm-mergeViewEditor"></div> // It is empty
<div class="cm-mergeViewEditor"></div> // It is empty
</div>
</div>
<div class="cm-editor cm-merge-b">
// div.cm-scroller only contains div.cm-content, when actually rendered DOM has div.cm-layer, div.cm-gutters etc.
<div tabindex="-1" class="cm-scroller">
<div class="cm-content">...</div>
</div>
</div>
From browser's Elements tab (actual rendered DOM):
<div class="cm-mergeView">
<div class="cm-mergeViewEditors">
<div class="cm-mergeViewEditor">
<div class="cm-editor cm-merge-a">
<div class="cm-gutters">...</div>
<div class="cm-scroller">...</div>
<div class="cm-layer cm-selectionLayer">...</div>
</div>
</div>
<div class="cm-mergeViewEditor">
<div class="cm-editor cm-merge-b">
...
</div>
</div>
</div>
</div>
✅ Additional Note
When I tested with ReactCodeMirror (not CodeMirrorMerge) using the onCreateEditor callback, I received the correct scrollDOM, and event listeners attached worked as expected.
🙋 Questions
-
Shouldn’t the dom and scrollDOM properties point to the actual scrollable container (with .cm-scroller, .cm-gutters, etc.)? Why is this happening?
-
Is there a better way to reliably access the rendered editor DOM for scroll syncing or event handling?
Thanks again for your great work — I’d love to hear your recommendations or whether this could be addressed in a future release.