Skip to content

Commit e76d00c

Browse files
committed
feat: clipboard image upload
1 parent d65ca9d commit e76d00c

File tree

4 files changed

+90
-13
lines changed

4 files changed

+90
-13
lines changed
Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,78 @@
1+
import { UploadImageArgs } from '@/api/routes/files'
2+
import { EditorSelection } from '@codemirror/state'
13
import { EditorView } from 'codemirror'
24

3-
export const handlePaste = (event: ClipboardEvent, view: EditorView) => {
4-
console.log('paste event')
5+
export const handlePaste = (
6+
event: ClipboardEvent,
7+
view: EditorView,
8+
upload: (args: UploadImageArgs) => Promise<string>,
9+
): void => {
510
const clipboardData = event.clipboardData
611
if (!clipboardData) return
7-
console.log('clipboardData', clipboardData)
12+
const text = clipboardData.getData('Text')
13+
14+
if (text) return
15+
16+
event.preventDefault()
17+
18+
const itemArray = []
19+
for (let i = 0; i < clipboardData.files.length; i++) {
20+
itemArray.push(clipboardData.files[i])
21+
}
22+
23+
const tempUrls: string[] = []
24+
const images = itemArray
25+
.filter((item) => item.type.includes('image'))
26+
.map((item) => {
27+
const tempUrl = URL.createObjectURL(item)
28+
const main = view.state.selection.main
29+
let insert = `![]()`
30+
31+
if (tempUrl) {
32+
insert = `![업로드중...](${tempUrl})\n`
33+
}
34+
35+
tempUrls.push(insert)
36+
view.dispatch({
37+
changes: {
38+
from: main.from,
39+
to: main.from,
40+
insert,
41+
},
42+
})
43+
return item
44+
})
45+
46+
const promises = images.map((item) => upload({ file: item, info: { type: 'book', refId: '' } }))
47+
48+
Promise.allSettled(promises).then((result) => {
49+
let lastCursorPosition = view.state.selection.main.from
50+
result.map((image, index) => {
51+
if (image.status !== 'fulfilled') return
52+
const tempUrl = tempUrls[index]
53+
const imageUrl = `![](${image.value})\n`
54+
55+
// replace temp url to real url
56+
const currentContent = view.state.doc.toString()
57+
const tempUrlIndex = currentContent.indexOf('![업로드중...]', lastCursorPosition)
58+
59+
if (tempUrlIndex !== -1) {
60+
view.dispatch({
61+
changes: {
62+
from: tempUrlIndex,
63+
to: tempUrlIndex + tempUrl.length,
64+
insert: imageUrl,
65+
},
66+
selection: EditorSelection.cursor(tempUrlIndex + imageUrl.length),
67+
})
68+
69+
lastCursorPosition = tempUrlIndex + imageUrl.length
70+
}
71+
})
72+
73+
// 모든 이미지 처리 후 커서를 마지막 이미지 다음으로 이동
74+
view.dispatch({
75+
selection: EditorSelection.cursor(lastCursorPosition),
76+
})
77+
})
878
}

packages/nextra-editor/src/components/markdown-editor/hooks/useCodemirror.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { hyperLink } from '@uiw/codemirror-extensions-hyper-link'
99
import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
1010
import { languages } from '@codemirror/language-data'
1111
import { handlePaste } from '../events/paste'
12+
import { useUpload } from '@/hooks/use-upload'
1213

1314
type Config = {
1415
autoFocus?: boolean
@@ -20,18 +21,14 @@ type Config = {
2021
maxWidth?: string | null
2122
}
2223

23-
// EditorView.domEventHandlers({
24-
// paste: handlePaste,
25-
// })
26-
2724
const External = Annotation.define<boolean>()
2825

2926
export const useCodemirror = (container: RefObject<HTMLElement>, config: Config = {}) => {
3027
const { theme: currentTheme } = useTheme()
3128
const { value, setValue, setStat } = useMarkdownEditor()
3229
const [state, setState] = useState<EditorState | null>(null)
3330
const [view, setView] = useState<EditorView | null>(null)
34-
31+
const { upload } = useUpload()
3532
const {
3633
autoFocus = true,
3734
height = null,
@@ -42,6 +39,13 @@ export const useCodemirror = (container: RefObject<HTMLElement>, config: Config
4239
maxWidth = null,
4340
} = config
4441

42+
const eventHandlers = EditorView.domEventHandlers({
43+
paste: (event, view) => handlePaste(event, view, upload),
44+
drop: (event, view) => {
45+
console.log('envet', event.dataTransfer?.files)
46+
},
47+
})
48+
4549
const defaultThemeOption = EditorView.theme({
4650
'&': {
4751
outline: 'none !important',
@@ -88,6 +92,7 @@ export const useCodemirror = (container: RefObject<HTMLElement>, config: Config
8892
hyperLink,
8993
updateListener,
9094
defaultThemeOption,
95+
eventHandlers,
9196
...defaultExtensions,
9297
]
9398

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const Toolbar = ({ state, view }: Props) => {
4747
excute(view)
4848
}
4949

50-
const onClickImageButton = async () => {
50+
const onImageUpload = async () => {
5151
if (!view || !state) return
5252
if (uploading) return
5353
const file = await onClickInput()
@@ -100,7 +100,7 @@ const Toolbar = ({ state, view }: Props) => {
100100
const commandMapper = (command: Partial<ToolbarCommand>) => {
101101
switch (command.name) {
102102
case 'image':
103-
onClickImageButton()
103+
onImageUpload()
104104
return
105105
default:
106106
onClick(command.execute!)

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ export const MarkdownEditorProvider = ({ children, value: { editorValue } }: Pro
3939
const [mdxSource, setMdxSource] = useState<MDXRemoteSerializeResult | null>(null)
4040
const [stat, setStat] = useState<Statistics | null>(null)
4141

42+
useEffect(() => {
43+
setValue(editorValue)
44+
}, [editorValue])
45+
4246
useEffect(() => {
4347
if (isError) {
4448
setIsError(false)
4549
}
46-
}, [value])
4750

48-
useEffect(() => {
4951
async function compileSource() {
5052
const result = await mdxCompiler(value, {
5153
onigHostUrl: process.env.NEXT_PUBLIC_CLIENT_HOST,
@@ -61,7 +63,7 @@ export const MarkdownEditorProvider = ({ children, value: { editorValue } }: Pro
6163
}
6264

6365
compileSource()
64-
}, [value, isError])
66+
}, [value])
6567

6668
const context: MarkdownEditorContext = {
6769
value,

0 commit comments

Comments
 (0)