1- import React , { useState , useRef , useImperativeHandle , Fragment } from 'react' ;
1+ import React , { useState , useRef , useImperativeHandle , Fragment , useEffect , useCallback } from 'react' ;
22import { markdown , markdownLanguage } from '@codemirror/lang-markdown' ;
33import { languages } from '@codemirror/language-data' ;
44import { EditorView , ViewUpdate } from '@codemirror/view' ;
5+ import * as events from '@uiw/codemirror-extensions-events' ;
56import CodeMirror , { ReactCodeMirrorProps , ReactCodeMirrorRef } from '@uiw/react-codemirror' ;
67import MarkdownPreview , { MarkdownPreviewProps } from '@uiw/react-markdown-preview' ;
78import ToolBar , { IToolBarProps } from './components/ToolBar' ;
@@ -32,6 +33,8 @@ export interface IMarkdownEditor extends ReactCodeMirrorProps {
3233 renderPreview ?: ( props : MarkdownPreviewProps , initVisible : boolean ) => React . ReactNode ;
3334 /** Preview expanded width @default `50%` */
3435 previewWidth ?: string ;
36+ /** Whether to enable scrolling */
37+ enableScroll ?: boolean ;
3538 /** Tool display settings. */
3639 toolbars ?: IToolBarProps [ 'toolbars' ] ;
3740 /** The tool on the right shows the settings. */
@@ -86,6 +89,7 @@ function MarkdownEditorInternal(
8689 visibleEditor = true ,
8790 hideToolbar = true ,
8891 toolbarBottom = false ,
92+ enableScroll = true ,
8993 previewProps = { } ,
9094 extensions = [ ] ,
9195 previewWidth = '50%' ,
@@ -97,6 +101,7 @@ function MarkdownEditorInternal(
97101 const container = useRef < HTMLDivElement > ( null ) ;
98102 const containerEditor = useRef < HTMLDivElement > ( null ) ;
99103 const preview = useRef < HTMLDivElement > ( null ) ;
104+ const active = useRef < 'editor' | 'preview' > ( 'editor' ) ;
100105
101106 useImperativeHandle (
102107 ref ,
@@ -115,9 +120,51 @@ function MarkdownEditorInternal(
115120 editorProps : { ...props , previewWidth } ,
116121 } ;
117122 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
119163 ? reExtensions
120164 : [ markdown ( { base : markdownLanguage , codeLanguages : languages } ) , scrollerStyle , ...extensions ] ;
165+ if ( enableScroll ) {
166+ extensionsData . push ( scrollExtensions ) ;
167+ }
121168 const clsPreview = `${ prefixCls } -preview` ;
122169 const cls = [ prefixCls , 'wmde-markdown-var' , className ] . filter ( Boolean ) . join ( ' ' ) ;
123170 previewProps [ 'source' ] = value ;
0 commit comments