1
- import React , { useState , useRef , useImperativeHandle , Fragment } from 'react' ;
1
+ import React , { useState , useRef , useImperativeHandle , Fragment , useEffect , useCallback } from 'react' ;
2
2
import { markdown , markdownLanguage } from '@codemirror/lang-markdown' ;
3
3
import { languages } from '@codemirror/language-data' ;
4
4
import { EditorView , ViewUpdate } from '@codemirror/view' ;
5
+ import * as events from '@uiw/codemirror-extensions-events' ;
5
6
import CodeMirror , { ReactCodeMirrorProps , ReactCodeMirrorRef } from '@uiw/react-codemirror' ;
6
7
import MarkdownPreview , { MarkdownPreviewProps } from '@uiw/react-markdown-preview' ;
7
8
import ToolBar , { IToolBarProps } from './components/ToolBar' ;
@@ -32,6 +33,8 @@ export interface IMarkdownEditor extends ReactCodeMirrorProps {
32
33
renderPreview ?: ( props : MarkdownPreviewProps , initVisible : boolean ) => React . ReactNode ;
33
34
/** Preview expanded width @default `50%` */
34
35
previewWidth ?: string ;
36
+ /** Whether to enable scrolling */
37
+ enableScroll ?: boolean ;
35
38
/** Tool display settings. */
36
39
toolbars ?: IToolBarProps [ 'toolbars' ] ;
37
40
/** The tool on the right shows the settings. */
@@ -86,6 +89,7 @@ function MarkdownEditorInternal(
86
89
visibleEditor = true ,
87
90
hideToolbar = true ,
88
91
toolbarBottom = false ,
92
+ enableScroll = true ,
89
93
previewProps = { } ,
90
94
extensions = [ ] ,
91
95
previewWidth = '50%' ,
@@ -97,6 +101,7 @@ function MarkdownEditorInternal(
97
101
const container = useRef < HTMLDivElement > ( null ) ;
98
102
const containerEditor = useRef < HTMLDivElement > ( null ) ;
99
103
const preview = useRef < HTMLDivElement > ( null ) ;
104
+ const active = useRef < 'editor' | 'preview' > ( 'editor' ) ;
100
105
101
106
useImperativeHandle (
102
107
ref ,
@@ -115,9 +120,51 @@ function MarkdownEditorInternal(
115
120
editorProps : { ...props , previewWidth } ,
116
121
} ;
117
122
const height = typeof codemirrorProps . height === 'number' ? `${ codemirrorProps . height } px` : codemirrorProps . height ;
118
- const extensionsData : IMarkdownEditor [ 'extensions' ] = reExtensions
123
+
124
+ const previewScrollHandle = useCallback (
125
+ ( event : Event ) => {
126
+ if ( ! enableScroll ) return ;
127
+ const target = event . target as HTMLDivElement ;
128
+ const percent = target . scrollTop / target . scrollHeight ;
129
+ if ( active . current === 'editor' && preview . current ) {
130
+ const previewHeihgt = preview . current ?. scrollHeight || 0 ;
131
+ preview . current ! . scrollTop = previewHeihgt * percent ;
132
+ } else if ( codeMirror . current && codeMirror . current . view ) {
133
+ const editorScrollDom = codeMirror . current . view . scrollDOM ;
134
+ const editorScrollHeihgt = codeMirror . current . view . scrollDOM . scrollHeight || 0 ;
135
+ editorScrollDom . scrollTop = editorScrollHeihgt * percent ;
136
+ }
137
+ } ,
138
+ [ enableScroll ] ,
139
+ ) ;
140
+ const mouseoverHandle = ( ) => ( active . current = 'preview' ) ;
141
+ const mouseleaveHandle = ( ) => ( active . current = 'editor' ) ;
142
+ useEffect ( ( ) => {
143
+ const $preview = preview . current ;
144
+ if ( $preview && enableScroll ) {
145
+ $preview . addEventListener ( 'mouseover' , mouseoverHandle , false ) ;
146
+ $preview . addEventListener ( 'mouseleave' , mouseleaveHandle , false ) ;
147
+ $preview . addEventListener ( 'scroll' , previewScrollHandle , false ) ;
148
+ }
149
+ return ( ) => {
150
+ if ( $preview && enableScroll ) {
151
+ $preview . removeEventListener ( 'mouseover' , mouseoverHandle ) ;
152
+ $preview . removeEventListener ( 'mouseleave' , mouseoverHandle ) ;
153
+ $preview . addEventListener ( 'mouseleave' , previewScrollHandle , false ) ;
154
+ }
155
+ } ;
156
+ } , [ preview , enableScroll , previewScrollHandle ] ) ;
157
+
158
+ const scrollExtensions = events . scroll ( {
159
+ scroll : previewScrollHandle ,
160
+ } ) ;
161
+
162
+ let extensionsData : IMarkdownEditor [ 'extensions' ] = reExtensions
119
163
? reExtensions
120
164
: [ markdown ( { base : markdownLanguage , codeLanguages : languages } ) , scrollerStyle , ...extensions ] ;
165
+ if ( enableScroll ) {
166
+ extensionsData . push ( scrollExtensions ) ;
167
+ }
121
168
const clsPreview = `${ prefixCls } -preview` ;
122
169
const cls = [ prefixCls , 'wmde-markdown-var' , className ] . filter ( Boolean ) . join ( ' ' ) ;
123
170
previewProps [ 'source' ] = value ;
0 commit comments