Skip to content

Commit 523b2c1

Browse files
committed
fix(merge): fix onChange props issue. (#502)
1 parent ac83cad commit 523b2c1

File tree

8 files changed

+169
-41
lines changed

8 files changed

+169
-41
lines changed

merge/src/Internal.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,29 @@ export const Internal = React.forwardRef((props: CodeMirrorMergeProps, ref?: Rea
6060
if (otherStore.collapseUnchanged !== collapseUnchanged) {
6161
opts.collapseUnchanged = collapseUnchanged;
6262
}
63-
if (Object.keys(opts).length && dispatch) {
64-
dispatch({ ...opts });
65-
}
66-
if (original && modified && editor.current) {
63+
if (Object.keys(opts).length && dispatch && original && modified && editor.current) {
6764
view.destroy();
68-
new MergeView({
69-
a: { ...original },
70-
b: { ...modified },
65+
const viewDefault = new MergeView({
66+
a: original,
67+
b: modified,
7168
parent: editor.current,
7269
...opts,
7370
});
71+
dispatch({ ...opts, renderRevertControl, view: viewDefault });
7472
}
7573
}
76-
}, [view, original, modified, editor, orientation, revertControls, highlightChanges, gutter, collapseUnchanged]);
74+
}, [
75+
view,
76+
original,
77+
modified,
78+
editor,
79+
orientation,
80+
revertControls,
81+
highlightChanges,
82+
gutter,
83+
collapseUnchanged,
84+
renderRevertControl,
85+
]);
7786

7887
const defaultClassNames = 'cm-merge-theme';
7988
return (

merge/src/Modified.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@ export const Modified = (props: ModifiedProps): JSX.Element | null => {
1717
const { extensions = [], onChange } = props;
1818
const { modified, view, dispatch } = useStore();
1919
const defaultExtensions = getDefaultExtensions();
20+
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
21+
if (
22+
vu.docChanged &&
23+
typeof onChange === 'function' &&
24+
// Fix echoing of the remote changes:
25+
// If transaction is market as remote we don't have to call `onChange` handler again
26+
!vu.transactions.some((tr) => tr.annotation(External))
27+
) {
28+
const doc = vu.state.doc;
29+
const value = doc.toString();
30+
onChange(value, vu);
31+
}
32+
});
33+
const extensionsData = [updateListener, ...defaultExtensions, ...extensions];
34+
const data: EditorStateConfig = { extensions: [...extensionsData] };
2035
useEffect(() => {
21-
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
22-
if (
23-
vu.docChanged &&
24-
typeof onChange === 'function' &&
25-
// Fix echoing of the remote changes:
26-
// If transaction is market as remote we don't have to call `onChange` handler again
27-
!vu.transactions.some((tr) => tr.annotation(External))
28-
) {
29-
const doc = vu.state.doc;
30-
const value = doc.toString();
31-
onChange(value, vu);
32-
}
33-
});
34-
const data: EditorStateConfig = { extensions: [updateListener, ...defaultExtensions, ...extensions] };
3536
if (modified?.doc !== props.value && view) {
3637
data.doc = props.value;
3738
dispatch!({ modified: { ...modified, ...data } });
3839
const modifiedDoc = view?.b.state.doc.toString();
3940
if (modifiedDoc !== props.value) {
4041
view.b.dispatch({
4142
changes: { from: 0, to: (modifiedDoc || '').length, insert: props.value || '' },
42-
effects: StateEffect.appendConfig.of([...defaultExtensions, ...extensions]),
43+
effects: StateEffect.appendConfig.of([...extensionsData]),
4344
annotations: [External.of(true)],
4445
});
4546
}

merge/src/Original.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,30 @@ export const Original = (props: OriginalProps): JSX.Element | null => {
1717
const { extensions = [], onChange } = props;
1818
const { original, view, dispatch } = useStore();
1919
const defaultExtensions = getDefaultExtensions();
20+
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
21+
if (
22+
vu.docChanged &&
23+
typeof onChange === 'function' &&
24+
// Fix echoing of the remote changes:
25+
// If transaction is market as remote we don't have to call `onChange` handler again
26+
!vu.transactions.some((tr) => tr.annotation(External))
27+
) {
28+
const doc = vu.state.doc;
29+
const value = doc.toString();
30+
onChange(value, vu);
31+
}
32+
});
33+
const extensionsData = [updateListener, ...defaultExtensions, ...extensions];
34+
const data: EditorStateConfig = { extensions: [...extensionsData] };
2035
useEffect(() => {
21-
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
22-
if (
23-
vu.docChanged &&
24-
typeof onChange === 'function' &&
25-
// Fix echoing of the remote changes:
26-
// If transaction is market as remote we don't have to call `onChange` handler again
27-
!vu.transactions.some((tr) => tr.annotation(External))
28-
) {
29-
const doc = vu.state.doc;
30-
const value = doc.toString();
31-
onChange(value, vu);
32-
}
33-
});
34-
const data: EditorStateConfig = { extensions: [updateListener, ...defaultExtensions, ...extensions] };
3536
if (original?.doc !== props.value && view) {
3637
data.doc = props.value;
3738
dispatch!({ original: { ...original, ...data } });
3839
const originalDoc = view?.a.state.doc.toString();
3940
if (originalDoc !== props.value) {
4041
view?.a.dispatch({
4142
changes: { from: 0, to: (originalDoc || '').length, insert: props.value || '' },
42-
effects: StateEffect.appendConfig.of([...defaultExtensions, ...extensions]),
43+
effects: StateEffect.appendConfig.of([...extensionsData]),
4344
});
4445
}
4546
}

www/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@
7272
"@uiw/react-back-to-top": "^1.1.2",
7373
"@uiw/react-codemirror": "4.20.1",
7474
"@uiw/react-github-corners": "~1.5.14",
75-
"@uiw/react-markdown-preview": "~4.1.0",
75+
"@uiw/react-markdown-preview": "^4.1.13",
7676
"@uiw/react-shields": "~1.1.3",
7777
"@uiw/reset.css": "~1.0.5",
7878
"@wcj/dark-mode": "~1.0.12",
7979
"code-example": "^3.3.6",
8080
"markdown-react-code-preview-loader": "^2.1.2",
8181
"react": "~18.2.0",
82-
"react-code-preview-layout": "^3.0.0",
82+
"react-code-preview-layout": "^3.0.1",
8383
"react-codemirror-merge": "4.20.1",
8484
"react-dom": "~18.2.0",
8585
"react-router-dom": "^6.3.0",

www/src/components/Markdown.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { FC, PropsWithChildren, useEffect, useRef } from 'react';
2+
import CodeLayout from 'react-code-preview-layout';
3+
import { getMetaId, isMeta, getURLParameters, CodeBlockData } from 'markdown-react-code-preview-loader';
4+
import MarkdownPreview, { MarkdownPreviewProps } from '@uiw/react-markdown-preview';
5+
import rehypeIgnore from 'rehype-ignore';
6+
import { CodeProps } from 'react-markdown/lib/ast-to-react';
7+
8+
const Preview = CodeLayout.Preview;
9+
const Code = CodeLayout.Code;
10+
const Toolbar = CodeLayout.Toolbar;
11+
12+
interface CodePreviewProps extends CodeProps {
13+
mdData?: CodeBlockData;
14+
}
15+
16+
const CodePreview: FC<PropsWithChildren<CodePreviewProps>> = ({ inline, node, ...props }) => {
17+
const $dom = useRef<HTMLDivElement>(null);
18+
const { mdData, ...rest } = props;
19+
// useEffect(() => {
20+
// if ($dom.current) {
21+
// const parentElement = $dom.current.parentElement;
22+
// if (parentElement && parentElement.parentElement) {
23+
// parentElement.parentElement.replaceChild($dom.current, parentElement);
24+
// }
25+
// }
26+
// }, [$dom]);
27+
const { 'data-meta': meta } = props as any;
28+
if (inline || !isMeta(meta)) {
29+
return <code {...rest} />;
30+
}
31+
const line = node.position?.start.line;
32+
const metaId = getMetaId(meta) || String(line);
33+
const Child = mdData?.components[`${metaId}`];
34+
if (metaId && typeof Child === 'function') {
35+
const code = mdData?.data[metaId].value || '';
36+
const param = getURLParameters(meta);
37+
return (
38+
<CodeLayout ref={$dom}>
39+
<Preview>
40+
<Child />
41+
</Preview>
42+
<Toolbar>{param.title || 'Example'}</Toolbar>
43+
<Code>
44+
<pre {...rest} />
45+
</Code>
46+
</CodeLayout>
47+
);
48+
}
49+
return <code {...rest} />;
50+
};
51+
52+
interface MarkdownProps extends MarkdownPreviewProps {
53+
mdData?: CodeBlockData;
54+
}
55+
56+
export default function Markdown(props: MarkdownProps) {
57+
const { mdData, ...rest } = props;
58+
return (
59+
<MarkdownPreview
60+
{...rest}
61+
style={{ paddingTop: 30 }}
62+
disableCopy={true}
63+
rehypePlugins={[rehypeIgnore]}
64+
source={props.source || ''}
65+
components={{
66+
code: (props) => <CodePreview {...props} mdData={mdData} />,
67+
}}
68+
/>
69+
);
70+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# onChange Example
2+
3+
```jsx mdx:preview
4+
import React, { useState } from 'react';
5+
import CodeMirrorMerge from 'react-codemirror-merge';
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 App() {
16+
const [value, setValue] = useState('');
17+
const [valueModified, setValueModified] = useState('');
18+
return (
19+
<div>
20+
<CodeMirrorMerge>
21+
<Original
22+
onChange={(value) => {
23+
setValue(value);
24+
}}
25+
value={doc}
26+
/>
27+
<Modified
28+
onChange={(value) => {
29+
setValueModified(value);
30+
}}
31+
value={doc.replace(/t/g, 'T') + 'Six'}
32+
/>
33+
</CodeMirrorMerge>
34+
<div style={{ display: 'flex' }}>
35+
<pre style={{ flex: 1 }}>{value} </pre>
36+
<pre style={{ backgroundColor: '#fff', flex: 1 }}>{valueModified} </pre>
37+
</div>
38+
</div>
39+
);
40+
}
41+
```

www/src/pages/theme/Preview.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Warpper } from '../../components/Warpper';
66
import { PreCode } from './themes/PreCode';
77
import { themeData } from './themes/Datas';
88
import { Sample } from './themes/Sample';
9+
import Markdown from '../../components/Markdown';
910

1011
interface PreviewProps {
1112
path?: any;
@@ -76,7 +77,7 @@ export const Preview: FC<PropsWithChildren<PreviewProps>> = (props) => {
7677
{childs.map((child, key) => {
7778
return cloneElement(child as any, { key, source: mdData?.source });
7879
})}
79-
{mdData && (previewDoc || !themePkg) && <MarkdownPreview source={mdData.source} />}
80+
{mdData && (previewDoc || !themePkg) && <Markdown source={mdData.source} mdData={mdData} />}
8081
{!previewDoc && themePkg && themeExtensionName && <Sample theme={extension} />}
8182
</Content>
8283
</Warpper>

www/src/router.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@ export const routes: MenuRouteObject[] = [
509509
</Preview>
510510
),
511511
},
512+
{
513+
path: 'onchange',
514+
label: 'onChange Example',
515+
element: <Preview path={() => import('./pages/merge/examples/Example.md')} />,
516+
},
512517
],
513518
},
514519
{

0 commit comments

Comments
 (0)