Skip to content

Commit 7a71754

Browse files
committed
WIP: sync scroll
1 parent a34c58d commit 7a71754

File tree

16 files changed

+128
-160
lines changed

16 files changed

+128
-160
lines changed

packages/markdown-editor/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
"@theguild/remark-mermaid": "0.0.6",
5959
"@theguild/remark-npm2yarn": "0.2.0",
6060
"@uiw/codemirror-extensions-basic-setup": "^4.22.1",
61-
"@uiw/codemirror-extensions-events": "^4.23.0",
6261
"@uiw/codemirror-extensions-hyper-link": "^4.22.1",
6362
"@uiw/codemirror-themes": "^4.22.1",
6463
"@uiw/codemirror-themes-all": "^4.22.1",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { EditorView } from 'codemirror'
2+
3+
export const handleKeydown = (event: Event, view: EditorView): void => {
4+
// handle preview scroll
5+
const preview = document.getElementById('markdown-editor-preview')
6+
if (!preview) return
7+
8+
const head = view.state.selection.main.head
9+
10+
const cursorLine = view.state.doc.lineAt(head).number
11+
const lastLine = view.state.doc.lineAt(view.state.doc.length).number
12+
13+
if (lastLine - cursorLine < 5) {
14+
const previewHeight = preview.scrollHeight ?? 0
15+
preview.scrollTop = previewHeight
16+
}
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { EditorView } from 'codemirror'
2+
3+
export const handleScroll = (event: Event, view: EditorView): void => {
4+
if (!event.target) return
5+
const preview = document.getElementById('markdown-editor-preview')
6+
if (!preview) return
7+
// TODO: 비율에 따라서 스크롤 위치를 조정해야함
8+
9+
if (view.scrollDOM.scrollTop > 100) {
10+
const previewHeight = preview.scrollHeight ?? 0
11+
preview.scrollTop = previewHeight
12+
}
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { handleDrop } from './handle-drop'
2+
export { handlePaste } from './handle-paste'
3+
export { handleScroll } from './handle-scroll'
4+
export { handleKeydown } from './handle-keydown'

packages/markdown-editor/src/components/markdown-editor/lib/getDefaultExtensions.ts renamed to packages/markdown-editor/src/components/markdown-editor/lib/get-default-extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { EditorView, keymap, placeholder } from '@codemirror/view'
33
import { indentWithTab } from '@codemirror/commands'
44
import { tags } from '@lezer/highlight'
55
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
6-
import { darkTheme, lightTheme } from './theme'
6+
import { darkTheme, lightTheme } from './editor-theme'
77
import { basicSetup, type BasicSetupOptions } from '@uiw/codemirror-extensions-basic-setup'
88
import { codeKeymap } from '../toolbar/commands/code'
99
import { boldKeymap } from '../toolbar/commands/bold'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { lightTheme, darkTheme } from './editor-theme'
2+
export { getDefaultExtensions } from './get-default-extensions'
3+
export { getEditorStat } from './get-editor-stat'
Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,36 @@
11
import cn from 'clsx'
2-
import { forwardRef, useEffect, useRef } from 'react'
3-
2+
import { useRef } from 'react'
43
import { Toolbar } from './toolbar'
5-
import { ReactCodeMirrorRef } from '@/types'
6-
import { Extension } from '@codemirror/state'
74
import { useCodemirror } from '@/hooks'
85

9-
interface MarkdownEditorProps {
10-
codeMirrorExtensions?: Extension[]
11-
}
12-
13-
export const MarkdownEditor = forwardRef<ReactCodeMirrorRef, MarkdownEditorProps>(
14-
({ codeMirrorExtensions = [] }, editorRef) => {
15-
const codemirror = useRef<HTMLDivElement | null>(null)
16-
const { state, view } = useCodemirror(codemirror, {
17-
autoFocus: true,
18-
minHeight: '100%',
19-
maxHeight: '100%',
20-
extension: codeMirrorExtensions,
21-
})
6+
interface MarkdownEditorProps {}
227

23-
useEffect(() => {
24-
if (!codemirror.current) return
25-
if (!view || !state) return
26-
if (typeof editorRef === 'function') {
27-
editorRef({ editor: codemirror.current, view, state })
28-
} else if (editorRef && 'current' in editorRef) {
29-
editorRef.current = {
30-
editor: codemirror.current,
31-
view,
32-
state,
33-
}
34-
}
35-
}, [editorRef, view, state])
8+
export const MarkdownEditor = ({}: MarkdownEditorProps) => {
9+
const codemirror = useRef<HTMLDivElement | null>(null)
10+
const { state, view } = useCodemirror(codemirror, {
11+
autoFocus: true,
12+
minHeight: '100%',
13+
maxHeight: '100%',
14+
})
3615

37-
const onClick = () => {
38-
view?.focus()
39-
}
16+
const onClick = () => {
17+
view?.focus()
18+
}
4019

41-
return (
42-
<>
43-
<Toolbar state={state} view={view} />
44-
<div
45-
className={cn('markdown-editor-codemirror')}
46-
onClick={onClick}
47-
ref={codemirror}
48-
suppressHydrationWarning={true}
49-
suppressContentEditableWarning={true}
50-
style={{
51-
height:
52-
'calc(100vh - (var(--nextra-navbar-height)) - var(--nextra-editor-toolbar-height))',
53-
}}
54-
/>
55-
</>
56-
)
57-
},
58-
)
20+
return (
21+
<>
22+
<Toolbar state={state} view={view} />
23+
<div
24+
className={cn('markdown-editor-codemirror')}
25+
onClick={onClick}
26+
ref={codemirror}
27+
suppressHydrationWarning={true}
28+
suppressContentEditableWarning={true}
29+
style={{
30+
height:
31+
'calc(100vh - (var(--nextra-navbar-height)) - var(--nextra-editor-toolbar-height))',
32+
}}
33+
/>
34+
</>
35+
)
36+
}
Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,39 @@
11
import cn from 'clsx'
22
import { useConfig } from '@/contexts'
3-
import { forwardRef, type ReactElement } from 'react'
3+
import { type ReactElement } from 'react'
44
import { MDXRemote } from 'next-mdx-remote'
55
import { getComponents } from '@/mdx-components'
66
import { useMarkdownEditor } from '@/contexts/markdown-editor'
77

88
interface MarkdownPreviewProps {}
99

10-
export const MarkdownPreview = forwardRef<HTMLDivElement, MarkdownPreviewProps>(
11-
(props, ref): ReactElement => {
12-
const config = useConfig()
13-
const { mdxSource } = useMarkdownEditor()
10+
export const MarkdownPreview = ({}: MarkdownPreviewProps): ReactElement => {
11+
const config = useConfig()
12+
const { mdxSource } = useMarkdownEditor()
1413

15-
if (!mdxSource) {
16-
return <div>Mdx source Loading...</div>
17-
}
14+
if (!mdxSource) {
15+
return <div>Mdx source Loading...</div>
16+
}
1817

19-
return (
20-
<div
21-
ref={ref}
22-
className={cn(
23-
'markdown-editor-preview',
24-
'markdown-editor-scrollbar nx-h-screen nx-overflow-y-auto nx-break-words nx-pb-16',
25-
)}
26-
>
27-
<main className="nx-mt-6 nx-w-full nx-min-w-0 nx-max-w-6xl nx-px-6 nx-pb-[8rem] nx-pt-6">
28-
<MDXRemote
29-
compiledSource={mdxSource.compiledSource}
30-
frontmatter={mdxSource.frontmatter}
31-
scope={mdxSource.scope}
32-
components={getComponents({
33-
isRawLayout: false,
34-
components: config.components,
35-
})}
36-
/>
37-
</main>
38-
</div>
39-
)
40-
},
41-
)
18+
return (
19+
<div
20+
id="markdown-editor-preview"
21+
className={cn(
22+
'markdown-editor-preview',
23+
'markdown-editor-scrollbar nx-h-screen nx-overflow-y-auto nx-break-words nx-pb-16',
24+
)}
25+
>
26+
<main className="nx-mt-6 nx-w-full nx-min-w-0 nx-max-w-6xl nx-px-6 nx-pb-[8rem] nx-pt-6">
27+
<MDXRemote
28+
compiledSource={mdxSource.compiledSource}
29+
frontmatter={mdxSource.frontmatter}
30+
scope={mdxSource.scope}
31+
components={getComponents({
32+
isRawLayout: false,
33+
components: config.components,
34+
})}
35+
/>
36+
</main>
37+
</div>
38+
)
39+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { MDXRemoteSerializeResult } from 'next-mdx-remote'
22
import React, { createContext, useContext, useEffect, useState } from 'react'
33
import { mdxCompiler } from '../mdx-compiler'
4-
import { Statistics } from '../components/markdown-editor/lib/getEditorStat'
4+
import { Statistics } from '../components/markdown-editor/lib/get-editor-stat'
55
import { ViewUpdate } from '@codemirror/view'
66

77
type MarkdownEditorContext = {

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import { getDefaultExtensions } from '../components/markdown-editor/lib/getDefaultExtensions'
1+
import { getDefaultExtensions, getEditorStat } from '../components/markdown-editor/lib'
22
import { Annotation, EditorState, Extension, StateEffect } from '@codemirror/state'
33
import { EditorView, ViewUpdate } from '@codemirror/view'
44
import { useTheme } from 'next-themes'
55
import { RefObject, useEffect, useMemo, useState } from 'react'
66
import { useMarkdownEditor } from '../contexts/markdown-editor'
7-
import { getEditorStat } from '../components/markdown-editor/lib/getEditorStat'
87
import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'
98
import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
109
import { languages } from '@codemirror/language-data'
11-
import { handlePaste } from '../components/markdown-editor/events/paste'
12-
13-
import { handleDrop } from '../components/markdown-editor/events/drop'
1410
import { useUpload } from './use-upload'
11+
import {
12+
handlePaste,
13+
handleDrop,
14+
handleScroll,
15+
handleKeydown,
16+
} from '@/components/markdown-editor/events'
1517

1618
type Config = {
1719
autoFocus?: boolean
@@ -49,6 +51,8 @@ export const useCodemirror = (container: RefObject<HTMLDivElement>, config: Conf
4951
EditorView.domEventHandlers({
5052
paste: (event, view) => handlePaste(event, view, upload),
5153
drop: (event, view) => handleDrop(event, view, upload),
54+
scroll: handleScroll,
55+
keydown: handleKeydown,
5256
}),
5357
[upload],
5458
)

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

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,17 @@ import { ActiveAnchorProvider } from '@/contexts'
44
import { useFSRoute } from '@/nextra/hooks'
55
import { normalizePages } from '@/nextra/normalize-pages'
66
import type { PageOpts } from '@/nextra/types'
7-
import { useCallback, useEffect, useMemo, useRef, type ReactElement, type ReactNode } from 'react'
7+
import { useMemo, type ReactElement, type ReactNode } from 'react'
88
import { Banner, Head, Header, Sidebar } from '@/components'
99
import { MarkdownPreview } from '@/components/markdown-preview'
1010
import { MarkdownEditor } from '@/components/markdown-editor'
11-
import { ReactCodeMirrorRef } from '@/types'
12-
import * as events from '@uiw/codemirror-extensions-events'
1311

1412
type MainProps = PageOpts & {
1513
children: ReactNode
1614
}
1715

1816
export const Main = ({ frontMatter, headings, pageMap }: MainProps): ReactElement => {
1917
const fsPath = useFSRoute()
20-
const editorRef = useRef<ReactCodeMirrorRef>(null)
21-
const previewRef = useRef<HTMLDivElement>(null)
22-
const active = useRef<'editor' | 'preview'>('editor')
23-
24-
useEffect(() => {
25-
console.log('editorRef', editorRef)
26-
}, [editorRef])
2718

2819
const { activeThemeContext, docsDirectories, flatDirectories, directories, topLevelNavbarItems } =
2920
useMemo(
@@ -41,50 +32,6 @@ export const Main = ({ frontMatter, headings, pageMap }: MainProps): ReactElemen
4132
const direction = 'ltr'
4233
const mainHeight = 'calc(100vh - (var(--nextra-navbar-height)))'
4334

44-
const onPreviewScroll = useCallback((event: Event) => {
45-
// const target = event.target as HTMLDivElement
46-
// const percent = target.scrollTop / target.scrollHeight
47-
48-
if (active.current === 'editor' && previewRef.current) {
49-
const previewHeight = previewRef.current?.scrollHeight || 0
50-
previewRef.current.scrollTop = previewHeight
51-
}
52-
// else if (editorRef.current && editorRef.current.view) {
53-
// const editorScrollDom = editorRef.current.view.scrollDOM
54-
// const editorScrollHeihgt = editorRef.current.view.scrollDOM.scrollHeight || 0
55-
// editorScrollDom.scrollTop = editorScrollHeihgt * percent
56-
// }
57-
}, [])
58-
59-
const onMouseOver = () => {
60-
console.log('onMouseOver')
61-
active.current = 'preview'
62-
}
63-
const onMouseLeave = () => {
64-
console.log('onMouseLeave')
65-
active.current = 'editor'
66-
}
67-
68-
useEffect(() => {
69-
const $preview = previewRef.current
70-
if (!$preview) return
71-
console.log('previewRef', $preview)
72-
console.log('add event listener')
73-
$preview.addEventListener('mouseover', onMouseOver)
74-
$preview.addEventListener('mouseleave', onMouseLeave)
75-
$preview.addEventListener('scroll', onPreviewScroll)
76-
77-
return () => {
78-
$preview.removeEventListener('mouseover', onMouseOver)
79-
$preview.removeEventListener('mouseleave', onMouseLeave)
80-
$preview.addEventListener('scroll', onPreviewScroll)
81-
}
82-
}, [previewRef, onPreviewScroll])
83-
84-
const scrollExtensions = events.scroll({
85-
scroll: onPreviewScroll,
86-
})
87-
8835
return (
8936
<div
9037
dir={direction}
@@ -115,10 +62,10 @@ export const Main = ({ frontMatter, headings, pageMap }: MainProps): ReactElemen
11562
/>
11663
<div className={cn('nx-flex nx-overflow-hidden')} style={{ width: 'calc(100% - 320px)' }}>
11764
<div className={cn('nextra-editor-container nx-h-[100%] nx-w-1/2')}>
118-
<MarkdownEditor ref={editorRef} codeMirrorExtensions={[scrollExtensions]} />
65+
<MarkdownEditor />
11966
</div>
12067
<div className={cn('nextra-preview-container nx-h-[100%] nx-w-1/2')}>
121-
<MarkdownPreview ref={previewRef} />
68+
<MarkdownPreview />
12269
</div>
12370
</div>
12471
</ActiveAnchorProvider>

0 commit comments

Comments
 (0)