Skip to content

Commit 4cd1111

Browse files
committed
feat: sync scorll by preview
1 parent b1927bc commit 4cd1111

File tree

6 files changed

+106
-52
lines changed

6 files changed

+106
-52
lines changed
Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
import { EditorView } from 'codemirror'
22
import { throttle } from 'throttle-debounce'
33

4-
export const handleScroll = throttle(100, (event: Event, view: EditorView): void => {
5-
if (!event.target) return
6-
const preview = document.getElementById('markdown-editor-preview')
7-
if (!preview) return
4+
export const handleScroll = throttle(
5+
150,
6+
(event: Event, view: EditorView, isEditorActive): void => {
7+
if (!isEditorActive) return
8+
if (!event.target) return
9+
const preview = document.getElementById('markdown-editor-preview')
10+
if (!preview) return
811

9-
if (view.scrollDOM.scrollTop < 200) {
10-
preview.scrollTop = 0
11-
return
12-
}
12+
if (view.scrollDOM.scrollTop < 200) {
13+
preview.scrollTop = 0
14+
return
15+
}
1316

14-
const head = view.state.selection.main.head
15-
const cursorLine = view.state.doc.lineAt(head).number
16-
const lastLine = view.state.doc.lineAt(view.state.doc.length).number
17-
if (lastLine - cursorLine < 15) {
18-
return
19-
}
17+
// const head = view.state.selection.main.head
18+
// const cursorLine = view.state.doc.lineAt(head).number
19+
// const lastLine = view.state.doc.lineAt(view.state.doc.length).number
2020

21-
const previewScrollHeight = preview.scrollHeight - preview.clientHeight
22-
const editorScrollHeight = view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight
21+
// if (lastLine - cursorLine < 5) return
2322

24-
// editor 영역에서 스크롤 비율 구하기
25-
const percent = view.scrollDOM.scrollTop / editorScrollHeight
23+
const previewScrollHeight = preview.scrollHeight - preview.clientHeight
24+
const editorScrollHeight = view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight
2625

27-
// preview 영역에 스크롤 위치 설정
28-
preview.scrollTop = percent * previewScrollHeight
29-
})
26+
// editor 영역에서 스크롤 비율 구하기
27+
const percent = view.scrollDOM.scrollTop / editorScrollHeight
28+
29+
// preview 영역에 스크롤 위치 설정
30+
preview.scrollTop = percent * previewScrollHeight
31+
},
32+
)

packages/markdown-editor/src/components/markdown-editor/markdown-editor.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import { useRef } from 'react'
33
import { Toolbar } from './toolbar'
44
import { useCodemirror } from '@/hooks'
55

6-
interface MarkdownEditorProps {
7-
height: string
8-
}
6+
interface MarkdownEditorProps {}
97

10-
export const MarkdownEditor = ({ height }: MarkdownEditorProps) => {
8+
export const MarkdownEditor = ({}: MarkdownEditorProps) => {
119
const codemirror = useRef<HTMLDivElement | null>(null)
1210
const { state, view } = useCodemirror(codemirror, {
1311
autoFocus: true,
@@ -23,12 +21,16 @@ export const MarkdownEditor = ({ height }: MarkdownEditorProps) => {
2321
<>
2422
<Toolbar state={state} view={view} />
2523
<div
24+
id="markdown-editor-codemirror"
2625
className={cn('markdown-editor-codemirror')}
2726
onClick={onClick}
2827
ref={codemirror}
2928
suppressHydrationWarning={true}
3029
suppressContentEditableWarning={true}
31-
style={{ height }}
30+
style={{
31+
height:
32+
'calc(100vh - (var(--nextra-navbar-height)) - var(--nextra-editor-toolbar-height))',
33+
}}
3234
/>
3335
</>
3436
)
Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,75 @@
11
import cn from 'clsx'
22
import { useConfig } from '@/contexts'
3-
import { type ReactElement } from 'react'
3+
import { useEffect, useRef, useState, type ReactElement } from 'react'
44
import { MDXRemote } from 'next-mdx-remote'
55
import { getComponents } from '@/mdx-components'
66
import { useMarkdownEditor } from '@/contexts/markdown-editor'
7+
import { throttle } from 'throttle-debounce'
78

8-
interface MarkdownPreviewProps {
9-
height: string
10-
}
9+
interface MarkdownPreviewProps {}
1110

12-
export const MarkdownPreview = ({ height }: MarkdownPreviewProps): ReactElement => {
11+
export const MarkdownPreview = ({}: MarkdownPreviewProps): ReactElement => {
1312
const config = useConfig()
14-
const { mdxSource } = useMarkdownEditor()
1513

16-
if (!mdxSource) {
17-
return <div>Mdx source Loading...</div>
18-
}
14+
const { mdxSource, setActive, active } = useMarkdownEditor()
15+
const preview = useRef<HTMLDivElement>(null)
16+
17+
useEffect(() => {
18+
const onScroll = throttle(150, (event: Event) => {
19+
if (active !== 'preview') return
20+
if (!preview.current) return
21+
if (!event.target) return
22+
23+
const editor = document.querySelector('.cm-scroller')
24+
if (!editor) return
25+
26+
const editorScrollHeight = editor.scrollHeight - editor.clientHeight
27+
const previewScrollHeight = preview.current.scrollHeight - preview.current.clientHeight
28+
const percent = preview.current.scrollTop / previewScrollHeight
29+
editor.scrollTop = percent * editorScrollHeight
30+
})
31+
32+
if (!preview.current) return
33+
preview.current.addEventListener('scroll', onScroll)
34+
35+
return () => {
36+
if (!preview.current) return
37+
preview.current.removeEventListener('scroll', onScroll)
38+
}
39+
}, [preview.current, active])
40+
41+
const onMouseOver = throttle(200, () => {
42+
setActive('preview')
43+
})
44+
45+
const onMouseLeave = throttle(200, () => {
46+
setActive('editor')
47+
})
48+
49+
useEffect(() => {
50+
if (!preview.current) return
51+
preview.current.addEventListener('mouseover', onMouseOver)
52+
preview.current.addEventListener('mouseleave', onMouseLeave)
53+
54+
return () => {
55+
if (!preview.current) return
56+
preview.current.removeEventListener('mouseover', onMouseOver)
57+
preview.current.removeEventListener('mouseleave', onMouseLeave)
58+
}
59+
}, [preview.current])
1960

2061
return (
21-
<>
22-
<div
23-
id="markdown-editor-preview"
24-
className={cn(
25-
'markdown-editor-preview',
26-
'markdown-editor-scrollbar nx-h-screen nx-overflow-y-auto nx-scroll-smooth nx-break-words',
27-
)}
28-
style={{ height }}
29-
>
62+
<div
63+
ref={preview}
64+
id="markdown-editor-preview"
65+
className={cn(
66+
'markdown-editor-preview',
67+
'markdown-editor-scrollbar nx-h-screen nx-overflow-y-auto nx-scroll-smooth nx-break-words',
68+
)}
69+
style={{ height: '100%', paddingBottom: '10rem' }}
70+
>
71+
{!mdxSource && <div>Mdx source Loading...</div>}
72+
{mdxSource && (
3073
<main className="nx-w-full nx-px-6 ">
3174
<MDXRemote
3275
compiledSource={mdxSource.compiledSource}
@@ -38,7 +81,7 @@ export const MarkdownPreview = ({ height }: MarkdownPreviewProps): ReactElement
3881
})}
3982
/>
4083
</main>
41-
</div>
42-
</>
84+
)}
85+
</div>
4386
)
4487
}

packages/markdown-editor/src/contexts/markdown-editor.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ type MarkdownEditorContext = {
1111
setMdxSource: (value: MDXRemoteSerializeResult | null) => void
1212
stat: Statistics | null
1313
setStat: (value: Statistics) => void
14+
active: 'editor' | 'preview'
15+
setActive: (value: 'editor' | 'preview') => void
1416
}
1517

1618
const MarkdownEditorConext = createContext<MarkdownEditorContext>({
@@ -20,6 +22,8 @@ const MarkdownEditorConext = createContext<MarkdownEditorContext>({
2022
setMdxSource: () => {},
2123
stat: null,
2224
setStat: () => {},
25+
active: 'editor',
26+
setActive: () => {},
2327
})
2428

2529
export function useMarkdownEditor() {
@@ -38,6 +42,7 @@ export const MarkdownEditorProvider = ({ children, value: { editorValue } }: Pro
3842
const [isError, setIsError] = useState<boolean>(false)
3943
const [mdxSource, setMdxSource] = useState<MDXRemoteSerializeResult | null>(null)
4044
const [stat, setStat] = useState<Statistics | null>(null)
45+
const [active, setActive] = useState<'editor' | 'preview'>('editor')
4146

4247
useEffect(() => {
4348
setValue(editorValue)
@@ -74,6 +79,8 @@ export const MarkdownEditorProvider = ({ children, value: { editorValue } }: Pro
7479
setMdxSource,
7580
stat,
7681
setStat,
82+
active,
83+
setActive,
7784
}
7885

7986
return <MarkdownEditorConext.Provider value={context}>{children}</MarkdownEditorConext.Provider>

packages/markdown-editor/src/hooks/use-codemirror.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const External = Annotation.define<boolean>()
3030

3131
export const useCodemirror = (container: RefObject<HTMLDivElement>, config: Config = {}) => {
3232
const { theme: currentTheme } = useTheme()
33-
const { value, setValue, setStat } = useMarkdownEditor()
33+
const { value, setValue, setStat, active } = useMarkdownEditor()
3434
const [state, setState] = useState<EditorState | null>(null)
3535
const [view, setView] = useState<EditorView | null>(null)
3636

@@ -51,10 +51,10 @@ export const useCodemirror = (container: RefObject<HTMLDivElement>, config: Conf
5151
EditorView.domEventHandlers({
5252
paste: (event, view) => handlePaste(event, view, upload),
5353
drop: (event, view) => handleDrop(event, view, upload),
54-
scroll: handleScroll,
54+
scroll: (event, view) => handleScroll(event, view, active === 'editor'),
5555
keydown: handleKeydown,
5656
}),
57-
[upload],
57+
[upload, active],
5858
)
5959

6060
const defaultThemeOption = useMemo(

packages/markdown-editor/src/layouts/main.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export const Main = ({ frontMatter, headings, pageMap }: MainProps): ReactElemen
3131
const themeContext = { ...activeThemeContext, ...frontMatter }
3232
const direction = 'ltr'
3333
const mainHeight = 'calc(100vh - (var(--nextra-navbar-height)))'
34-
const height = 'calc(100vh - (var(--nextra-navbar-height)) - var(--nextra-editor-toolbar-height))'
3534
return (
3635
<div
3736
dir={direction}
@@ -64,10 +63,10 @@ export const Main = ({ frontMatter, headings, pageMap }: MainProps): ReactElemen
6463
<div
6564
className={cn('markdown-editor-container nx-relative nx-flex nx-w-1/2 nx-flex-col')}
6665
>
67-
<MarkdownEditor height={height} />
66+
<MarkdownEditor />
6867
</div>
6968
<div className={cn('markdown-preview-container nx-flex nx-w-1/2')}>
70-
<MarkdownPreview height={height} />
69+
<MarkdownPreview />
7170
</div>
7271
</main>
7372
</ActiveAnchorProvider>

0 commit comments

Comments
 (0)