Skip to content

Commit b84b763

Browse files
committed
fix(merge): fix onChange issue. (#566)
1 parent 92c4abe commit b84b763

File tree

5 files changed

+252
-3
lines changed

5 files changed

+252
-3
lines changed
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
'use client';
2+
3+
import { Fragment, useRef, useState } from 'react';
4+
import CodeMirrorMerge, { CodeMirrorMergeProps } from 'react-codemirror-merge';
5+
import { langs } from '@uiw/codemirror-extensions-langs';
6+
import { originalCode, modifiedCode } from './code';
7+
8+
const Original = CodeMirrorMerge.Original;
9+
const Modified = CodeMirrorMerge.Modified;
10+
11+
export const MergeExample = () => {
12+
const [orientation, setOrientation] = useState<CodeMirrorMergeProps['orientation']>('a-b');
13+
const [revertControls, setRevertControls] = useState<CodeMirrorMergeProps['revertControls']>();
14+
const [highlightChanges, setHighlightChanges] = useState<CodeMirrorMergeProps['highlightChanges']>(true);
15+
const [gutter, setGutter] = useState<CodeMirrorMergeProps['gutter']>(true);
16+
const [collapseUnchanged, setCollapseUnchanged] = useState<CodeMirrorMergeProps['collapseUnchanged']>();
17+
const handleOrientation = (evn: React.ChangeEvent<HTMLSelectElement>) => {
18+
setOrientation(evn.target.value as CodeMirrorMergeProps['orientation']);
19+
};
20+
const [originalValue, setOriginalValue] = useState(originalCode);
21+
const [modifiedValue, setModifiedValue] = useState(modifiedCode);
22+
const random = useRef<number>();
23+
const click = () => {
24+
random.current = Math.floor(Math.random() * 101);
25+
const code = '// hello world' + random.current + '\n' + originalCode;
26+
setOriginalValue(code);
27+
};
28+
return (
29+
<Fragment>
30+
<button onClick={click}>Change Original Value {random.current}</button>
31+
<CodeMirrorMerge
32+
orientation={orientation}
33+
revertControls={revertControls}
34+
collapseUnchanged={collapseUnchanged}
35+
highlightChanges={highlightChanges}
36+
// destroyRerender={false}
37+
gutter={gutter}
38+
style={{ height: 300, overflow: 'auto' }}
39+
theme="dark"
40+
>
41+
<Original
42+
value={originalValue}
43+
extensions={[langs.javascript()]}
44+
onChange={(val) => {
45+
setOriginalValue(val);
46+
// console.log('::::::::::', val)
47+
}}
48+
/>
49+
<Modified
50+
value={modifiedValue}
51+
extensions={[
52+
langs.javascript(),
53+
// EditorView.editable.of(false),
54+
// EditorState.readOnly.of(true)
55+
]}
56+
onChange={(val) => {
57+
setModifiedValue(val);
58+
}}
59+
/>
60+
</CodeMirrorMerge>
61+
<label>
62+
Orientation
63+
<select onChange={handleOrientation} defaultValue={orientation}>
64+
<option value="">please orientation choose</option>
65+
<option value="a-b">a-b</option>
66+
<option value="b-a">b-a</option>
67+
</select>
68+
</label>
69+
<br />
70+
<label>
71+
Revert buttons
72+
<select
73+
defaultValue={revertControls}
74+
onChange={(evn) => setRevertControls(evn.target.value as CodeMirrorMergeProps['revertControls'])}
75+
>
76+
<option value="">please revertControls choose</option>
77+
<option value="a-to-b">a-to-b</option>
78+
<option value="b-to-a">b-to-a</option>
79+
</select>
80+
</label>
81+
<br />
82+
<label>
83+
Highlight changes
84+
<input
85+
type="checkbox"
86+
checked={!!highlightChanges}
87+
onChange={(evn) => setHighlightChanges(evn.target.checked)}
88+
/>
89+
</label>
90+
<br />
91+
<label>
92+
Gutter markers
93+
<input type="checkbox" checked={!!gutter} onChange={(evn) => setGutter(evn.target.checked)} />
94+
</label>
95+
<label>
96+
Collapse unchanged code
97+
<input
98+
type="checkbox"
99+
checked={!!collapseUnchanged}
100+
onChange={(evn) => setCollapseUnchanged(evn.target.checked ? {} : undefined)}
101+
/>
102+
</label>
103+
</Fragment>
104+
);
105+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
export const originalCode = `// The player has a position, a size, and a current speed.
2+
class Player {
3+
size = new Vec(0.8, 1.5);
4+
5+
constructor(pos, speed) {
6+
this.pos = pos;
7+
this.speed = speed;
8+
}
9+
10+
static create(pos) {
11+
return new Player(pos.plus(new Vec(0, -0.5)), new Vec(0, 0));
12+
}
13+
}
14+
15+
// Lava block. When you touch it, you die.
16+
class Lava {
17+
size = new Vec(1, 1)
18+
19+
constructor(pos, speed, reset) {
20+
this.pos = pos;
21+
this.speed = speed;
22+
this.reset = reset;
23+
}
24+
25+
static horizontal(pos) {
26+
return new Lava(pos, new Vec(2, 0));
27+
}
28+
29+
static vertical(pos) {
30+
return new Lava(pos, new Vec(0, 2));
31+
}
32+
33+
static drip(pos) {
34+
return new Lava(pos, new Vec(0, 3), pos);
35+
}
36+
}
37+
`;
38+
39+
export const modifiedCode = `class Player {
40+
get type() { return "player" }
41+
42+
constructor(pos, speed) {
43+
this.pos = pos;
44+
this.speed = speed;
45+
}
46+
47+
static create(pos) {
48+
return new Player(pos.plus(new Vec(0, -0.5)), new Vec(0, 0));
49+
}
50+
}
51+
52+
class Lava {
53+
constructor(pos, speed, reset) {
54+
this.pos = pos;
55+
this.speed = speed;
56+
this.reset = reset;
57+
}
58+
59+
get type() { return "lava"; }
60+
61+
static create(pos, ch) {
62+
if (ch == "=") {
63+
return new Lava(pos, new Vec(2, 0));
64+
} else if (ch == "|") {
65+
return new Lava(pos, new Vec(0, 2));
66+
} else if (ch == "v") {
67+
return new Lava(pos, new Vec(0, 3), pos);
68+
}
69+
}
70+
}
71+
72+
Player.prototype.size = new Vec(0.8, 1.5);
73+
Lava.prototype.size = new Vec(1, 1);
74+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import CodeMirrorMerge from 'react-codemirror-merge';
5+
import { abyss } from '@uiw/codemirror-theme-abyss';
6+
7+
const Original = CodeMirrorMerge.Original;
8+
const Modified = CodeMirrorMerge.Modified;
9+
let doc = `one
10+
two
11+
three
12+
four
13+
five`;
14+
15+
export default function Editor() {
16+
const [value, setValue] = useState(doc);
17+
const [valueModified, setValueModified] = useState(doc.replace(/t/g, 'T') + 'Six');
18+
return (
19+
<div>
20+
<CodeMirrorMerge theme={abyss}>
21+
<Original
22+
value={value}
23+
onChange={(value) => {
24+
setValue(value);
25+
}}
26+
/>
27+
<Modified
28+
value={valueModified}
29+
onChange={(value) => {
30+
setValueModified(value);
31+
}}
32+
/>
33+
</CodeMirrorMerge>
34+
<div style={{ display: 'flex', marginTop: 10 }}>
35+
<pre style={{ flex: 1 }}>{value} </pre>
36+
<pre style={{ flex: 1 }}>{valueModified} </pre>
37+
</div>
38+
</div>
39+
);
40+
}

example/nextjs/src/app/merg/page.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// import Editor from './_editor';
2+
import { MergeExample } from './_editor/Com';
3+
4+
export default function Merg() {
5+
return (
6+
<div>
7+
<MergeExample />
8+
</div>
9+
);
10+
}

merge/src/Internal.tsx

+23-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const Internal = React.forwardRef<InternalRef, CodeMirrorMergeProps>((pro
2222
highlightChanges,
2323
gutter,
2424
collapseUnchanged,
25-
destroyRerender,
25+
destroyRerender = true,
2626
renderRevertControl,
2727
...elmProps
2828
} = props;
@@ -96,8 +96,10 @@ export const Internal = React.forwardRef<InternalRef, CodeMirrorMergeProps>((pro
9696
});
9797
}
9898
}
99-
if (destroyRerender) {
100-
view.current?.destroy();
99+
if (destroyRerender && view.current) {
100+
const originalFrom = view.current.a.state.selection.ranges[0].from;
101+
const modifiedFrom = view.current.b.state.selection.ranges[0].from;
102+
view.current.destroy();
101103
view.current = new MergeView({
102104
a: {
103105
...original,
@@ -116,6 +118,24 @@ export const Internal = React.forwardRef<InternalRef, CodeMirrorMergeProps>((pro
116118
parent: editor.current!,
117119
...opts,
118120
});
121+
if (originalFrom) {
122+
view.current.a.focus();
123+
view.current.a.dispatch({
124+
selection: {
125+
anchor: originalFrom,
126+
head: originalFrom,
127+
},
128+
});
129+
}
130+
if (modifiedFrom) {
131+
view.current.b.focus();
132+
view.current.b.dispatch({
133+
selection: {
134+
anchor: modifiedFrom,
135+
head: modifiedFrom,
136+
},
137+
});
138+
}
119139
}
120140
}, [view, theme, editor.current, original, modified, originalExtension, modifiedExtension, destroyRerender]);
121141

0 commit comments

Comments
 (0)